Backend and frontend changes
Libsaria events now support starting specific callback functions in a background thread. This replaces starting a specific event in the background. I have a library tab that is added to the main window through use of the ocarina.add_tab function. I have new tests for walking the tree and running multiple threads with locks.
This commit is contained in:
parent
66a8c5169e
commit
15cc5d22f0
|
@ -36,8 +36,10 @@ def startup():
|
|||
global plugin
|
||||
import music
|
||||
import plugin
|
||||
event.start("PRESTART")
|
||||
music.init()
|
||||
plugin.load_all()
|
||||
event.start("POSTSTART")
|
||||
|
||||
|
||||
def shutdown():
|
||||
|
|
|
@ -14,12 +14,18 @@ PLAYLIST = 1
|
|||
QUEUE = 2
|
||||
|
||||
import collection
|
||||
source = collection.Collection()
|
||||
library = collection.Collection()
|
||||
|
||||
def new_source(path, bg=True):
|
||||
global source
|
||||
global library
|
||||
path = expand(path)
|
||||
if not exists(path):
|
||||
return 0
|
||||
return call("NEWSOURCE", source.scan, arg=path, bg=bg)
|
||||
return call("NEWSOURCE", library.scan, path)
|
||||
|
||||
|
||||
def walk_library():
|
||||
global library
|
||||
for track in library:
|
||||
yield track
|
||||
|
||||
|
|
|
@ -10,8 +10,14 @@ ins_tree = None
|
|||
|
||||
class Collection:
|
||||
def __init__(self):
|
||||
pass
|
||||
self.tree = None
|
||||
#self.load()
|
||||
|
||||
def __iter__(self):
|
||||
if not self.tree:
|
||||
self.load()
|
||||
for track in self.tree.walk():
|
||||
yield track
|
||||
|
||||
def scan(self, path):
|
||||
print "Scanning path:", path
|
||||
|
@ -23,10 +29,8 @@ class Collection:
|
|||
ins_index = self.index.insert
|
||||
ins_tree = self.tree. insert
|
||||
self.update(path)
|
||||
self.tree.sort()
|
||||
self.save()
|
||||
|
||||
|
||||
def reset(self):
|
||||
print "Resetting collection ... "
|
||||
from table import Table
|
||||
|
@ -36,11 +40,17 @@ class Collection:
|
|||
self.index = Index()
|
||||
self.tree = Tree()
|
||||
|
||||
|
||||
def save(self):
|
||||
from libsaria import data
|
||||
data.save((self.table, self.index, self.tree), "library", "")
|
||||
|
||||
def load(self):
|
||||
from libsaria import data
|
||||
objects = data.load("library", "")
|
||||
if objects == None:
|
||||
self.reset()
|
||||
return
|
||||
(self.table, self.index, self.tree) = objects
|
||||
|
||||
|
||||
def insert(self, file, filepath):
|
||||
|
@ -63,7 +73,6 @@ class Collection:
|
|||
list.append(id)
|
||||
ins_tree(list, tags, audio_prop, filepath)
|
||||
|
||||
|
||||
def update(self, path):
|
||||
global tag
|
||||
global audio
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# Bryan Schumaker (8 / 12 / 2010)
|
||||
|
||||
get = dict.get
|
||||
get = dict.get
|
||||
|
||||
class TagNode(dict):
|
||||
def __init__(self):
|
||||
def __init__(self, id):
|
||||
dict.__init__(self)
|
||||
self["id"] = id
|
||||
|
||||
def insert(self, tag_list, tags, audio, filepath):
|
||||
self["artist"] = tags.artist
|
||||
|
@ -15,30 +16,22 @@ class TagNode(dict):
|
|||
self["filepath"] = filepath
|
||||
self["playcount"] = 0
|
||||
self["length"] = audio.length
|
||||
#self.artist = tags.artist
|
||||
#self.album = tags.album
|
||||
#self.title = tags.title
|
||||
#self.year = tags.year
|
||||
|
||||
#self.filepath = filepath
|
||||
#self.playcount = 0
|
||||
#self.length = audio.length
|
||||
|
||||
def sort(self, level):
|
||||
pass
|
||||
def walk(self):
|
||||
yield self
|
||||
|
||||
|
||||
class Tree(dict):
|
||||
def __init__(self):
|
||||
dict.__init__(self)
|
||||
self.sorted_keys = None
|
||||
|
||||
def sort(self, level=0):
|
||||
self.sorted_keys = self.keys()
|
||||
self.sorted_keys.sort()
|
||||
space = ' '*level
|
||||
for key in self.sorted_keys:
|
||||
self[key].sort(level+1)
|
||||
def walk(self):
|
||||
keys = self.keys()
|
||||
keys.sort()
|
||||
get_item = self.__getitem__
|
||||
for key in keys:
|
||||
for track in get_item(key).walk():
|
||||
yield track
|
||||
|
||||
def insert(self, tag_list, tags, audio, filepath):
|
||||
if len(tag_list) == 0:
|
||||
|
@ -49,7 +42,7 @@ class Tree(dict):
|
|||
node = get(self, tag, None)
|
||||
if node == None:
|
||||
if len(tag_list) == 1:
|
||||
node = TagNode()
|
||||
node = TagNode(tag)
|
||||
else:
|
||||
node = Tree()
|
||||
self[tag] = node
|
||||
|
|
|
@ -6,8 +6,8 @@ plugin = None
|
|||
|
||||
PROTO = pickle.HIGHEST_PROTOCOL
|
||||
|
||||
def save(item, file, ext="pickle"):
|
||||
file = "%s.%s" % (path.join(path.saria_dir(),file), ext)
|
||||
def save(item, file, ext=".pickle"):
|
||||
file = "%s%s" % (path.join(path.saria_dir(),file), ext)
|
||||
savefile(item, file)
|
||||
|
||||
|
||||
|
@ -18,8 +18,9 @@ def savefile(item, file):
|
|||
f.close()
|
||||
|
||||
|
||||
def load(file, ext="pickle"):
|
||||
file = "%s.%s" % (path.join(path.saria_dir(),file),ext)
|
||||
def load(file, ext=".pickle"):
|
||||
file = "%s%s" % (path.join(path.saria_dir(),file),ext)
|
||||
print file
|
||||
return loadfile(file)
|
||||
|
||||
|
||||
|
|
|
@ -1,26 +1,16 @@
|
|||
# Bryan Schumaker (8/7/2010)
|
||||
|
||||
events = dict()
|
||||
from threading import Thread
|
||||
events = dict()
|
||||
threads = None
|
||||
|
||||
start_thread = None
|
||||
|
||||
|
||||
class EventThread(Thread):
|
||||
def __init__(self, func, action, arg):
|
||||
Thread.__init__(self)
|
||||
self.func = func
|
||||
self.action = action
|
||||
self.arg = arg
|
||||
|
||||
def run(self):
|
||||
res = call("BGTHREAD", self.func, self.arg)
|
||||
start("POST%s" % self.action, res)
|
||||
|
||||
|
||||
def invite(key, func):
|
||||
def invite(key, func, bg=False):
|
||||
global events
|
||||
if key not in events:
|
||||
events[key] = set()
|
||||
events[key].add(func)
|
||||
events[key].add((func,bg))
|
||||
|
||||
|
||||
def uninvite(key, func):
|
||||
|
@ -30,50 +20,43 @@ def uninvite(key, func):
|
|||
events[key].discard(func)
|
||||
|
||||
|
||||
def start(key, arg=None):
|
||||
def start_thread_once(func, *args):
|
||||
global threads
|
||||
global start_thread
|
||||
import threads
|
||||
start_thread = threads.background
|
||||
return threads.background(func, *args)
|
||||
start_thread = start_thread_once
|
||||
|
||||
|
||||
def start(key, *args):
|
||||
global events
|
||||
if key not in events:
|
||||
return
|
||||
for func in events[key]:
|
||||
for func, bg in events[key]:
|
||||
try:
|
||||
if arg == None:
|
||||
func()
|
||||
if bg == False:
|
||||
func(*args)
|
||||
else:
|
||||
func(arg)
|
||||
start_thread(func, *args)
|
||||
except Exception,e:
|
||||
print
|
||||
print "===== EVENT ERROR ====="
|
||||
print "Event:", key
|
||||
print "Function:", func
|
||||
print "Args:", arg
|
||||
print "Args:", args
|
||||
print e
|
||||
print "The function has been uninvited from this event"
|
||||
print "====="
|
||||
|
||||
|
||||
def start_thread(func, action, arg):
|
||||
try:
|
||||
th = EventThread(func, action, arg)
|
||||
th.start()
|
||||
return True
|
||||
except Exception,e:
|
||||
print e
|
||||
return False
|
||||
|
||||
|
||||
def call(action, func=None, arg=None, bg=False):
|
||||
start("PRE%s" % action, arg)
|
||||
def call(action, func=None, *args):
|
||||
start("PRE%s" % action, *args)
|
||||
if func == None:
|
||||
return None
|
||||
|
||||
if bg == True:
|
||||
res = start_thread(func, action, arg)
|
||||
else:
|
||||
if arg == None:
|
||||
res = func()
|
||||
else:
|
||||
res = func(arg)
|
||||
start("POST%s" % action, res)
|
||||
res = func(*args)
|
||||
start("POST%s" % action, res)
|
||||
return res
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# Bryan Schumaker (8/19/2010)
|
||||
|
||||
import threading
|
||||
Thread = threading.Thread
|
||||
Mutex = threading.Lock
|
||||
|
||||
|
||||
mutex_mutex = Mutex()
|
||||
mutexes = dict()
|
||||
|
||||
|
||||
class BG_Thread(Thread):
|
||||
def __init__(self, func, *args):
|
||||
Thread.__init__(self)
|
||||
self.func = func
|
||||
self.args = args
|
||||
def run(self):
|
||||
self.func(*self.args)
|
||||
|
||||
|
||||
def background(func, *args):
|
||||
try:
|
||||
th = BG_Thread(func, *args)
|
||||
th.start()
|
||||
return th
|
||||
except Exception,e:
|
||||
print e
|
||||
return None
|
||||
|
||||
|
||||
def get_mutex(lock_name):
|
||||
global mutexes
|
||||
global Mutex
|
||||
global mutex_mutex
|
||||
mutex_mutex.acquire()
|
||||
lock = mutexes.get(lock_name, None)
|
||||
if lock == None:
|
||||
lock = Mutex()
|
||||
mutexes[lock_name] = lock
|
||||
mutex_mutex.release()
|
||||
return lock
|
16
ocarina.py
16
ocarina.py
|
@ -1,20 +1,26 @@
|
|||
#!/usr/bin/python
|
||||
# Bryan Schumaker (8/13/2010)
|
||||
|
||||
import datetime
|
||||
now = datetime.datetime.now
|
||||
before = now()
|
||||
|
||||
import libsaria
|
||||
import ocarina
|
||||
|
||||
from ocarina import collection
|
||||
|
||||
libsaria.init()
|
||||
libsaria.init_pref("window_size", (800,600))
|
||||
|
||||
prefs = libsaria.prefs
|
||||
|
||||
import gtk
|
||||
label = gtk.Label("Ocarina 4.1")
|
||||
label.show()
|
||||
|
||||
win = ocarina.get_window(prefs["window_size"])
|
||||
ocarina.set_window_title("Ocarina 4.1")
|
||||
ocarina.add_tab("Test", label)
|
||||
|
||||
ocarina.add_tab("Library", collection.Library())
|
||||
|
||||
after = now()
|
||||
print "Startup took:", after-before
|
||||
|
||||
ocarina.startup()
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
# Bryan Schumaker (8/13/2010)
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import libsaria
|
||||
|
||||
gobject.threads_init()
|
||||
|
||||
# Lazy loaded modules
|
||||
window = None
|
||||
tabs = None
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# Bryan Schumaker (8/16/2010)
|
||||
|
||||
import ocarina
|
||||
import list
|
||||
|
||||
|
||||
libsaria = ocarina.libsaria
|
||||
from libsaria import collection
|
||||
|
||||
event = ocarina.libsaria.event
|
||||
gtk = ocarina.gtk
|
||||
|
||||
|
||||
class Collection(gtk.ScrolledWindow):
|
||||
def __init__(self):
|
||||
gtk.ScrolledWindow.__init__(self)
|
||||
self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
self.list = list.List()
|
||||
self.add(self.list)
|
||||
self.show()
|
||||
|
||||
def populate(self, func):
|
||||
self.list.freeze()
|
||||
append = self.list.append
|
||||
for track in func():
|
||||
#print track
|
||||
get = track.__getitem__
|
||||
append([get("id"),
|
||||
get("title"),
|
||||
get("length"),
|
||||
get("artist"),
|
||||
get("album"),
|
||||
get("year"),
|
||||
get("playcount")])
|
||||
self.list.thaw()
|
||||
|
||||
|
||||
|
||||
class Library(Collection):
|
||||
def __init__(self):
|
||||
Collection.__init__(self)
|
||||
libsaria.event.invite("POSTSTART", self.populate, bg=True)
|
||||
|
||||
def populate(self):
|
||||
import datetime
|
||||
before = datetime.datetime.now()
|
||||
Collection.populate(self, collection.walk_library)
|
||||
after = datetime.datetime.now()
|
||||
print "Populating took: %s" % (after-before)
|
|
@ -0,0 +1,49 @@
|
|||
# Bryan Schumaker (8/15/2010)
|
||||
|
||||
import ocarina
|
||||
gtk = ocarina.gtk
|
||||
gobject = ocarina.gobject
|
||||
|
||||
#UNI = gobject.TYPE_UNICHAR
|
||||
|
||||
class List(gtk.TreeView):
|
||||
def __init__(self):
|
||||
gtk.TreeView.__init__(self)
|
||||
self.list = gtk.ListStore(int, str, str, str, str, int, int)
|
||||
self.append = self.list.append
|
||||
|
||||
cell = gtk.CellRendererText()
|
||||
cell.set_fixed_height_from_font(1)
|
||||
|
||||
cols = ["Id", "Title", "Length", "Artist", "Album", "Year", "Played"]
|
||||
colw = [ 2, 300, 60, 125, 125, 50, 70]
|
||||
for index, label in enumerate(cols):
|
||||
col = gtk.TreeViewColumn(label, cell)
|
||||
col.add_attribute(cell, 'text', index)
|
||||
col.set_resizable(True)
|
||||
col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
|
||||
col.set_min_width(2)
|
||||
col.set_max_width(700)
|
||||
col.set_fixed_width(colw[index])
|
||||
if label!="Id" and label!="Played":
|
||||
self.append_column(col)
|
||||
|
||||
self.set_rules_hint(True)
|
||||
self.set_tooltip_column(len(cols)-1)
|
||||
self.filter_model = self.list.filter_new()
|
||||
self.filter_model.set_visible_func(self.set_visible)
|
||||
|
||||
self.set_model(self.filter_model)
|
||||
self.show_all()
|
||||
|
||||
def freeze(self):
|
||||
self.set_model(None)
|
||||
self.freeze_child_notify()
|
||||
|
||||
def thaw(self):
|
||||
self.set_model(self.filter_model)
|
||||
self.thaw_child_notify()
|
||||
|
||||
def set_visible(self, a, b):
|
||||
return True
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
# Bryan Schumaker (8/14/2010)
|
||||
|
||||
import ocarina
|
||||
|
||||
music = ocarina.libsaria.music
|
||||
gtk = ocarina.gtk
|
||||
webkit = None
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Bryan Schumaker (8/19/2010)
|
||||
|
||||
from libsaria import threads
|
||||
|
||||
def test(a, b, c):
|
||||
lock = threads.get_mutex("print")
|
||||
lock.acquire()
|
||||
print "a:", a
|
||||
print "b:", b
|
||||
print "c:", c
|
||||
lock.release()
|
||||
|
||||
def test2():
|
||||
lock = threads.get_mutex("print")
|
||||
lock.acquire()
|
||||
print "No args!"
|
||||
lock.release()
|
||||
|
||||
|
||||
a = threads.background(test, 1, 2, 3)
|
||||
b = threads.background(test2)
|
||||
|
||||
if a and a.is_alive():
|
||||
a.join()
|
||||
if b.is_alive():
|
||||
b.join()
|
|
@ -0,0 +1,18 @@
|
|||
# Bryan Schumaker (8/17/2010)
|
||||
|
||||
from libsaria import collection
|
||||
|
||||
count = 0
|
||||
list = []
|
||||
append = list.append
|
||||
|
||||
for track in collection.walk_library():
|
||||
get = track.__getitem__
|
||||
append([get("id"), get("title"), get("length"),
|
||||
get("artist"),
|
||||
get("album"),
|
||||
get("year"),
|
||||
get("playcount")])
|
||||
count += 1
|
||||
|
||||
print "Counted: %s files" % count
|
Loading…
Reference in New Issue