I am trying to write a small audio player in PyQt4. This is a part of the code I've written:
class Player(QMainWindow):
def __init__(self, fileLoc, parent = None):
super(QMainWindow, self).__init__()
self.totTime = 0
self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
self.mediaObject = Phonon.MediaObject(self)
self.mediaObject.setTickInterval(1000)
self.mediaObject.tick.connect(self.tick)
self.mediaObject.stateChanged.connect(self.stateChanged)
Phonon.createPath(self.mediaObject, self.audioOutput)
#Define Play, Pause and Stop actions
self.playAction = QAction(self.style().standardIcon(QStyle.SP_MediaPlay),
"Play", self, enabled = False, triggered = self.mediaObject.play)
self.pauseAction = QAction(self.style().standardIcon(QStyle.SP_MediaPause),
"Pause", self, enabled = False, triggered = self.mediaObject.pause)
self.stopAction = QAction(self.style().standardIcon(QStyle.SP_MediaStop),
"Stop", self, enabled = False, triggered = self.mediaObject.stop)
#Initiate User Interface
self.userInterface()
self.timeDisp.display('00:00')
self.mediaObject.setCurrentSource(Phonon.MediaSource(fileLoc))
self.mediaObject.play()
def tick(self, time):
self.displayTime = QTime(0, (time / 60000) % 60, (time / 1000) % 60)
self.timeDisp.display(self.displayTime.toString('mm:ss'))
My problem is, I am unable to figure out how to get the total duration of the file being currently played. I have tried printing the output of mediObject.totalTime() at the end of init(). But it returned -1 for all the videos. mediObject.totalTime() inside tick() is returning incorrect duration (10 - 15 seconds longer than the actual duration).
Also, I may have to access the value of total duration from outside the class. How can I do this?
Thanks in advance.
You could connect the pause, play, and stop actions with other functions:
in the class __init__:
self.total_time = 0
self.playing = False
self.play_action = QAction(self.style().standardIcon((QStyle.SP_MediaPlay),"play",self)
self.play_action.triggered.connect(self.play_triggered_event)
and define the rest of the actions in a similar manner, connecting the triggered field of each QAction to a function
def play_triggered_event(self):
if not self.playing:
self.mediaObject.play
self.playing = True
self.start_time = time.clock()
def pause_triggered_event(self):
if self.playing:
self.mediaObject.pause
self.playing = False
self.total_time += (time.clock() - self.start_time)
def stop_triggered_event(self):
if self.playing:
self.mediaObject.stop
self.playing = False
print "Total time elapsed: " + str(self.total_time)
Basically, it's about saving some program state when the actions that affect the time elapsed are triggered.
To get the total time outside of the class, write an accessor for the class:
def get_total_time(self):
return self.total_time
Related
I want to run a function every 5 minutes starting at a particular wall clock time.
I have implemeneted threading.timer which runs every 5 minutes (interval) but I don't know how to start this timer at a particular time.
from threading import Timer
class BSOJobScheduler(object):
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.is_running = False
self.start()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
self._timer.daemon = True
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
Please advise
The easiest way to that is using Time.time() so I will use that.
You start timer like this:
start = Time.time()
And end like this:
end = Time.time()
Then to get value of time you will just subtract start from end like this:
value = end - start
You have to do like this becouse start = Time.time() gets value from computer so your next step is get another value from computer end = Time.time() and just subtract it.
And you just checking if value >= 300 (300secounds is 5minutes).
One of the threads could do someting like this:
INTERVAL = 5 * 60
next_run = time.monotonic() # better than time.time()
while True: # or while not stop_condition:
my_function() # should not take more time than the INTERVAL
next_run += INTERVAL
sleep_time = next_run - time.monotonic()
time.sleep(sleep_time)
How can I start and stop a thread with my poor thread class?
It is in loop, and I want to restart it again at the beginning of the code. How can I do start-stop-restart-stop-restart?
My class:
import threading
class Concur(threading.Thread):
def __init__(self):
self.stopped = False
threading.Thread.__init__(self)
def run(self):
i = 0
while not self.stopped:
time.sleep(1)
i = i + 1
In the main code, I want:
inst = Concur()
while conditon:
inst.start()
# After some operation
inst.stop()
# Some other operation
You can't actually stop and then restart a thread since you can't call its start() method again after its run() method has terminated. However you can make one pause and then later resume its execution by using a threading.Condition variable to avoid concurrency problems when checking or changing its running state.
threading.Condition objects have an associated threading.Lock object and methods to wait for it to be released and will notify any waiting threads when that occurs. Here's an example derived from the code in your question which shows this being done. In the example code I've made the Condition variable a part of Thread subclass instances to better encapsulate the implementation and avoid needing to introduce additional global variables:
from __future__ import print_function
import threading
import time
class Concur(threading.Thread):
def __init__(self):
super(Concur, self).__init__()
self.iterations = 0
self.daemon = True # Allow main to exit even if still running.
self.paused = True # Start out paused.
self.state = threading.Condition()
def run(self):
self.resume()
while True:
with self.state:
if self.paused:
self.state.wait() # Block execution until notified.
# Do stuff...
time.sleep(.1)
self.iterations += 1
def pause(self):
with self.state:
self.paused = True # Block self.
def resume(self):
with self.state:
self.paused = False
self.state.notify() # Unblock self if waiting.
class Stopwatch(object):
""" Simple class to measure elapsed times. """
def start(self):
""" Establish reference point for elapsed time measurements. """
self.start_time = time.time()
return self
#property
def elapsed_time(self):
""" Seconds since started. """
try:
return time.time() - self.start_time
except AttributeError: # Wasn't explicitly started.
self.start_time = time.time()
return 0
MAX_RUN_TIME = 5 # Seconds.
concur = Concur()
stopwatch = Stopwatch()
print('Running for {} seconds...'.format(MAX_RUN_TIME))
concur.start()
while stopwatch.elapsed_time < MAX_RUN_TIME:
concur.resume()
# Can also do other concurrent operations here...
concur.pause()
# Do some other stuff...
# Show Concur thread executed.
print('concur.iterations: {}'.format(concur.iterations))
This is David Heffernan's idea fleshed-out. The example below runs for 1 second, then stops for 1 second, then runs for 1 second, and so on.
import time
import threading
import datetime as DT
import logging
logger = logging.getLogger(__name__)
def worker(cond):
i = 0
while True:
with cond:
cond.wait()
logger.info(i)
time.sleep(0.01)
i += 1
logging.basicConfig(level=logging.DEBUG,
format='[%(asctime)s %(threadName)s] %(message)s',
datefmt='%H:%M:%S')
cond = threading.Condition()
t = threading.Thread(target=worker, args=(cond, ))
t.daemon = True
t.start()
start = DT.datetime.now()
while True:
now = DT.datetime.now()
if (now-start).total_seconds() > 60: break
if now.second % 2:
with cond:
cond.notify()
The implementation of stop() would look like this:
def stop(self):
self.stopped = True
If you want to restart, then you can just create a new instance and start that.
while conditon:
inst = Concur()
inst.start()
#after some operation
inst.stop()
#some other operation
The documentation for Thread makes it clear that the start() method can only be called once for each instance of the class.
If you want to pause and resume a thread, then you'll need to use a condition variable.
The following python3 code is what I might have expected to generate a few calls to the doit event, followed by a call to the terminate event, which would stop the app, but only the first event fires. What am I doing wrong?
from circuits import Component, Event, Debugger
import time
times = []
class doit(Event):
"""doit Event"""
class terminate(Event):
"""terminate Event"""
class App(Component):
def __init__(self):
super().__init__()
self.interval = .1
self.last = 0
self.count = 0
def doit(self, origin):
times.append(("%s from A at %.03f" % (origin, time.time())))
self.count += 1
self.last = time.time()
def generate_events(self, event):
if self.last + self.interval < time.time():
event.stop()
self.fire(doit('ge'))
if self.count >= 5:
event.stop()
self.fire(terminate())
def terminate(self):
self.stop()
(Debugger() + App()).run()
print("\n".join(times))
I got the same behavior using event.reduce_time_left(0) instead of event.stop().
The main error in the example is that it doesn't reduce_time_left(time.time() - self.last + self.interval) when there is nothing to do.
generate_events fires once when the app starts. Each generator needs to set reduce_time_left() to the maximum reasonable time before firing again - so that it will certainly fire again by that time - whether something is generated or not. Reducing the time to 0 indicates that this cycle is complete (and events need to be fired).
The preferred solution uses Timer to implement the time functionality, reducing this example to the logic to display how it works.
from circuits import BaseComponent, Event, Timer, Debugger, handler
import time
class doit(Event):
"""doit Event"""
class App(BaseComponent):
timer = Timer(.1, doit('A'), persist=True)
def __init__(self):
super().__init__()
self.count = 0
#handler("doit")
def _doit(self, origin):
print("%s from A at %.03f" % (origin, time.time()))
self.count += 1
if self.count > 4:
self.stop()
(App() + Debugger()).run()
I just finished a software and want to test it now with help of python's TestCase. The method test_show_video should start a QTimer. It is connected to test_run_iteration, which sums up an index up by one every 0,1 seconds. Before assert is called, the method has three seconds time to sum up the index, so it should be bigger then zero. But it is not. Does anybody have a clue? In fact, the timeout-connection to the timer seems to be wrong.
try:
app = QtGui.QApplication(sys.argv)
except RuntimeError:
app = QtCore.QCoreApplication.instance()
class TestProgressPresenter(TestCase):
def test_show_video(self):
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.test_run_iteration)
self.timer.start(100)
self.index = 0
millis = start_time = int(round(time.time() * 1000))
while start_time + 3000 > millis:
millis = int(round(time.time() * 1000))
assert self.index > 0
def test_run_iteration(self):
self.index += 1
Here you are blocking the Qt event loop during the while loop for 3 seconds. Actually the timer's timeout signal is not called until control returns to the event loop and that's when the while is completed and test_show_video is finished.
If you want the timer to get triggered you should make sure that the events are processed during the while loop checks. For that you can have some thing like :
while start_time + 3000 > millis:
millis = int(round(time.time() * 1000))
QApplication.processEvents(QEventLoop.AllEvents)
It can be even easier to wait for some seconds using a local event loop :
self.loop = QtCore.QEventLoop()
QTimer.singleShot(3000, loop.quit)
loop.exec()
assert self.index > 0
I'm writing a module for quick and easy timing in a Python program. The idea is that little instances of clocks can be created throughout the code. These clocks are available as objects that can be started, stopped, started again and queried. Any clocks instantiated are added to a module list of all clocks. At the conclusion of the program, a printout of all clocks (either a listing of all clocks or the means of all similar clocks) can be requested of this list.
I've got a lot of it working, but the timing of functions is still causing me difficulty. Specifically, the times measured for the functions are measured as 0 using either explicit clocks or using a decorator, when the time measured for functions 1 and 1 should be ~3 seconds and ~4 seconds respectively.
I suspect that I am not retaining the clock attribute _startTimeTmp in an appropriate way (it can be reset for the purposes of internal calculations).
I would really appreciate some guidance on getting the timers working correctly. I've got myself a bit confused on how to solve it!
I'm aware that the code may look slightly long, but I've minimized it as much as I know how to without obscuring the vision of what I'm trying to do overall (so that any suggestions proposed don't remove critical functionality). I do think it's reasonably clear how it works, at least.
module (shijian.py):
from __future__ import division
import os
import time
import uuid as uuid
import datetime
import inspect
import functools
def _main():
global clocks
clocks = Clocks()
def time_UTC(
style = None
):
return(
style_datetime_object(
datetimeObject = datetime.datetime.utcnow(),
style = style
)
)
def style_datetime_object(
datetimeObject = None,
style = "YYYY-MM-DDTHHMMSS"
):
# filename safe
if style == "YYYY-MM-DDTHHMMSSZ":
return(datetimeObject.strftime('%Y-%m-%dT%H%M%SZ'))
# microseconds
elif style == "YYYY-MM-DDTHHMMSSMMMMMMZ":
return(datetimeObject.strftime('%Y-%m-%dT%H%M%S%fZ'))
# elegant
elif style == "YYYY-MM-DD HH:MM:SS UTC":
return(datetimeObject.strftime('%Y-%m-%d %H:%M:%SZ'))
# UNIX time in seconds with second fraction
elif style == "UNIX time S.SSSSSS":
return(
(datetimeObject -\
datetime.datetime.utcfromtimestamp(0)).total_seconds()
)
# UNIX time in seconds rounded
elif style == "UNIX time S":
return(
int((datetimeObject -\
datetime.datetime.utcfromtimestamp(0)).total_seconds())
)
# filename safe
else:
return(datetimeObject.strftime('%Y-%m-%dT%H%M%SZ'))
def UID():
return(str(uuid.uuid4()))
class Clock(object):
def __init__(
self,
name = None,
start = True
):
self._name = name
self._start = start # Boolean start clock on instantiation
self._startTime = None # internal (value to return)
self._startTimeTmp = None # internal (value for calculations)
self._stopTime = None # internal (value to return)
self._updateTime = None # internal
# If no name is specified, generate a unique one.
if self._name is None:
self._name = UID()
# If a global clock list is detected, add a clock instance to it.
if "clocks" in globals():
clocks.add(self)
self.reset()
if self._start:
self.start()
def start(self):
self._startTimeTmp = datetime.datetime.utcnow()
self._startTime = datetime.datetime.utcnow()
def stop(self):
self._updateTime = None
self._startTimeTmp = None
self._stopTime = datetime.datetime.utcnow()
# Update the clock accumulator.
def update(self):
if self._updateTime:
self.accumulator += (
datetime.datetime.utcnow() - self._updateTime
)
else:
self.accumulator += (
datetime.datetime.utcnow() - self._startTimeTmp
)
self._updateTime = datetime.datetime.utcnow()
def reset(self):
self.accumulator = datetime.timedelta(0)
self._startTimeTmp = None
# If the clock has a start time, add the difference between now and the
# start time to the accumulator and return the accumulation. If the clock
# does not have a start time, return the accumulation.
def elapsed(self):
if self._startTimeTmp:
self.update()
return(self.accumulator)
def name(self):
return(self._name)
def time(self):
return(self.elapsed().total_seconds())
def startTime(self):
if self._startTime:
return(style_datetime_object(datetimeObject = self._startTime))
else:
return("none")
def stopTime(self):
if self._stopTime:
return(style_datetime_object(datetimeObject = self._stopTime))
else:
return("none")
def report(
self
):
string = "clock attribute".ljust(39) + "value"
string += "\nname".ljust(40) + self.name()
string += "\ntime start (s)".ljust(40) + self.startTime()
string += "\ntime stop (s)".ljust(40) + self.stopTime()
string += "\ntime elapsed (s)".ljust(40) + str(self.time())
string += "\n"
return(string)
def printout(self):
print(self.report())
def timer(function):
##functools.wraps(function)
def decoration(
*args,
**kwargs
):
arguments = inspect.getcallargs(function, *args, **kwargs)
clock = Clock(name = function.__name__)
result = function(*args, **kwargs)
clock.stop()
return(decoration)
class Clocks(object):
def __init__(
self
):
self._listOfClocks = []
self._defaultReportStyle = "statistics"
def add(
self,
clock
):
self._listOfClocks.append(clock)
def report(
self,
style = None
):
if style is None:
style = self._defaultReportStyle
if self._listOfClocks != []:
if style == "statistics":
# Create a dictionary of clock types with corresponding lists of
# times for all instances.
dictionaryOfClockTypes = {}
# Get the names of all clocks and add them to the dictionary.
for clock in self._listOfClocks:
dictionaryOfClockTypes[clock.name()] = []
# Record the values of all clocks for their respective names in
# the dictionary.
for clock in self._listOfClocks:
dictionaryOfClockTypes[clock.name()].append(clock.time())
# Create a report, calculating the average value for each clock
# type.
string = "clock type".ljust(39) + "mean time (s)"
for name, values in dictionaryOfClockTypes.iteritems():
string += "\n" +\
str(name).ljust(39) + str(sum(values)/len(values))
string += "\n"
elif style == "full":
# Create a report, listing the values of all clocks.
string = "clock".ljust(39) + "time (s)"
for clock in self._listOfClocks:
string += "\n" +\
str(clock.name()).ljust(39) + str(clock.time())
string += "\n"
else:
string = "no clocks"
return(string)
def printout(
self,
style = None
):
if style is None:
style = self._defaultReportStyle
print(self.report(style = style))
_main()
main code example (examples.py):
import shijian
import time
import inspect
def main():
print("create clock alpha")
alpha = shijian.Clock(name = "alpha")
print("clock alpha start time: {time}".format(time = alpha.startTime()))
print("sleep 2 seconds")
time.sleep(2)
print("clock alpha current time (s): {time}".format(time = alpha.time()))
print("\ncreate clock beta")
beta = shijian.Clock(name = "beta")
print("clock beta start time: {time}".format(time = beta.startTime()))
print("clock beta stop time: {time}".format(time = beta.stopTime()))
print("sleep 2 seconds")
time.sleep(2)
print("clock beta current time (s): {time}".format(time = beta.time()))
print("stop clock beta")
beta.stop()
print("clock beta start time: {time}".format(time = beta.startTime()))
print("clock beta stop time: {time}".format(time = beta.stopTime()))
print("sleep 2 seconds")
time.sleep(2)
print("clock beta start time: {time}".format(time = beta.startTime()))
print("clock beta stop time: {time}".format(time = beta.stopTime()))
print("clock beta current time (s): {time}".format(time = beta.time()))
print("\nclock beta printout:\n")
beta.printout()
print("create two gamma clocks")
gamma = shijian.Clock(name = "gamma")
gamma = shijian.Clock(name = "gamma")
print("sleep 2 seconds")
time.sleep(2)
print("\ncreate two unnamed clocks")
delta = shijian.Clock()
epsilon = shijian.Clock()
print("sleep 2 seconds")
time.sleep(2)
print("\nrun function 1 (which is timed using internal clocks)")
function1()
print("\nrun function 2 (which is timed using a decorator)")
function2()
print("\nclocks full printout:\n")
shijian.clocks.printout(style = "full")
print("clocks statistics printout:\n")
shijian.clocks.printout()
def function1():
functionName = inspect.stack()[0][3]
clock = shijian.Clock(name = functionName)
print("initiate {functionName}".format(functionName = functionName))
time.sleep(3)
print("terminate {functionName}".format(functionName = functionName))
clock.stop()
#shijian.timer
def function2():
functionName = inspect.stack()[0][3]
print("initiate {functionName}".format(functionName = functionName))
time.sleep(4)
print("terminate {functionName}".format(functionName = functionName))
if __name__ == '__main__':
main()
example terminal output:
create clock alpha
clock alpha start time: 2015-01-03T090124Z
sleep 2 seconds
clock alpha current time (s): 2.000887
create clock beta
clock beta start time: 2015-01-03T090126Z
clock beta stop time: none
sleep 2 seconds
clock beta current time (s): 2.002123
stop clock beta
clock beta start time: 2015-01-03T090126Z
clock beta stop time: 2015-01-03T090128Z
sleep 2 seconds
clock beta start time: 2015-01-03T090126Z
clock beta stop time: 2015-01-03T090128Z
clock beta current time (s): 2.002123
clock beta printout:
clock attribute value
name beta
time start (s) 2015-01-03T090126Z
time stop (s) 2015-01-03T090128Z
time elapsed (s) 2.002123
create two gamma clocks
sleep 2 seconds
create two unnamed clocks
sleep 2 seconds
run function 1 (which is timed using internal clocks)
initiate function1
terminate function1
run function 2 (which is timed using a decorator)
initiate function2
terminate function2
clocks full printout:
clock time (s)
alpha 17.023659
beta 2.002123
gamma 11.018138
gamma 11.018138
1919f9de-85ce-48c9-b1c8-5164f3a2633e 9.017148
d24c818c-f4e6-48d0-ad72-f050a5cf86d3 9.017027
function1 0.0
function2 0.0
clocks statistics printout:
clock type mean time (s)
function1 0.0
function2 0.0
1919f9de-85ce-48c9-b1c8-5164f3a2633e 9.017283
beta 2.002123
alpha 17.023834
d24c818c-f4e6-48d0-ad72-f050a5cf86d3 9.017163
gamma 11.0182835
The Clock does not get updated when it is stopped. The minimal fix is:
def stop(self):
self.update()
self._updateTime = None
self._startTimeTmp = None
self._stopTime = datetime.datetime.utcnow()
You have three other errors:
You should test for None by identity (if foo is not None) not truthiness (if foo), to avoid issues with False-y values that aren't None;
shijian.timer doesn't return result, so although the timing will work you'll break any code that expects a return from the decorated function; and
If you want the code to work in Python 2 and 3, you can't use dict.iteritems, which doesn't exist in the latter. If you only want it to work in Python 2, either from __future__ import print_function or use print whatever rather than print(whatever).
Additionally, your code is not at all compliant with the style guide (or, worse, even internally consistent - compare the definition of Clock.start with that of Clock.report, for example).
There is also room for improvement in the design and functionality (e.g. Clock.name could be a #property, and I would separate the table printing from the generation of results). You should consider submitting your code for Code Review, once you have:
completed;
tested; and
style-guide-complianced it
(you might find using pylint helpful for the latter).
Finally, I assume you're doing this for learning purposes rather than because you need the functionality, as Python has its own profilers.