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
|
global plugin
|
||||||
import music
|
import music
|
||||||
import plugin
|
import plugin
|
||||||
|
event.start("PRESTART")
|
||||||
music.init()
|
music.init()
|
||||||
plugin.load_all()
|
plugin.load_all()
|
||||||
|
event.start("POSTSTART")
|
||||||
|
|
||||||
|
|
||||||
def shutdown():
|
def shutdown():
|
||||||
|
|
|
@ -14,12 +14,18 @@ PLAYLIST = 1
|
||||||
QUEUE = 2
|
QUEUE = 2
|
||||||
|
|
||||||
import collection
|
import collection
|
||||||
source = collection.Collection()
|
library = collection.Collection()
|
||||||
|
|
||||||
def new_source(path, bg=True):
|
def new_source(path, bg=True):
|
||||||
global source
|
global library
|
||||||
path = expand(path)
|
path = expand(path)
|
||||||
if not exists(path):
|
if not exists(path):
|
||||||
return 0
|
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:
|
class Collection:
|
||||||
def __init__(self):
|
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):
|
def scan(self, path):
|
||||||
print "Scanning path:", path
|
print "Scanning path:", path
|
||||||
|
@ -23,10 +29,8 @@ class Collection:
|
||||||
ins_index = self.index.insert
|
ins_index = self.index.insert
|
||||||
ins_tree = self.tree. insert
|
ins_tree = self.tree. insert
|
||||||
self.update(path)
|
self.update(path)
|
||||||
self.tree.sort()
|
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
print "Resetting collection ... "
|
print "Resetting collection ... "
|
||||||
from table import Table
|
from table import Table
|
||||||
|
@ -36,11 +40,17 @@ class Collection:
|
||||||
self.index = Index()
|
self.index = Index()
|
||||||
self.tree = Tree()
|
self.tree = Tree()
|
||||||
|
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
from libsaria import data
|
from libsaria import data
|
||||||
data.save((self.table, self.index, self.tree), "library", "")
|
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):
|
def insert(self, file, filepath):
|
||||||
|
@ -63,7 +73,6 @@ class Collection:
|
||||||
list.append(id)
|
list.append(id)
|
||||||
ins_tree(list, tags, audio_prop, filepath)
|
ins_tree(list, tags, audio_prop, filepath)
|
||||||
|
|
||||||
|
|
||||||
def update(self, path):
|
def update(self, path):
|
||||||
global tag
|
global tag
|
||||||
global audio
|
global audio
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
# Bryan Schumaker (8 / 12 / 2010)
|
# Bryan Schumaker (8 / 12 / 2010)
|
||||||
|
|
||||||
get = dict.get
|
get = dict.get
|
||||||
|
|
||||||
class TagNode(dict):
|
class TagNode(dict):
|
||||||
def __init__(self):
|
def __init__(self, id):
|
||||||
dict.__init__(self)
|
dict.__init__(self)
|
||||||
|
self["id"] = id
|
||||||
|
|
||||||
def insert(self, tag_list, tags, audio, filepath):
|
def insert(self, tag_list, tags, audio, filepath):
|
||||||
self["artist"] = tags.artist
|
self["artist"] = tags.artist
|
||||||
|
@ -15,30 +16,22 @@ class TagNode(dict):
|
||||||
self["filepath"] = filepath
|
self["filepath"] = filepath
|
||||||
self["playcount"] = 0
|
self["playcount"] = 0
|
||||||
self["length"] = audio.length
|
self["length"] = audio.length
|
||||||
#self.artist = tags.artist
|
|
||||||
#self.album = tags.album
|
|
||||||
#self.title = tags.title
|
|
||||||
#self.year = tags.year
|
|
||||||
|
|
||||||
#self.filepath = filepath
|
def walk(self):
|
||||||
#self.playcount = 0
|
yield self
|
||||||
#self.length = audio.length
|
|
||||||
|
|
||||||
def sort(self, level):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Tree(dict):
|
class Tree(dict):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
dict.__init__(self)
|
dict.__init__(self)
|
||||||
self.sorted_keys = None
|
|
||||||
|
|
||||||
def sort(self, level=0):
|
def walk(self):
|
||||||
self.sorted_keys = self.keys()
|
keys = self.keys()
|
||||||
self.sorted_keys.sort()
|
keys.sort()
|
||||||
space = ' '*level
|
get_item = self.__getitem__
|
||||||
for key in self.sorted_keys:
|
for key in keys:
|
||||||
self[key].sort(level+1)
|
for track in get_item(key).walk():
|
||||||
|
yield track
|
||||||
|
|
||||||
def insert(self, tag_list, tags, audio, filepath):
|
def insert(self, tag_list, tags, audio, filepath):
|
||||||
if len(tag_list) == 0:
|
if len(tag_list) == 0:
|
||||||
|
@ -49,7 +42,7 @@ class Tree(dict):
|
||||||
node = get(self, tag, None)
|
node = get(self, tag, None)
|
||||||
if node == None:
|
if node == None:
|
||||||
if len(tag_list) == 1:
|
if len(tag_list) == 1:
|
||||||
node = TagNode()
|
node = TagNode(tag)
|
||||||
else:
|
else:
|
||||||
node = Tree()
|
node = Tree()
|
||||||
self[tag] = node
|
self[tag] = node
|
||||||
|
|
|
@ -6,8 +6,8 @@ plugin = None
|
||||||
|
|
||||||
PROTO = pickle.HIGHEST_PROTOCOL
|
PROTO = pickle.HIGHEST_PROTOCOL
|
||||||
|
|
||||||
def save(item, file, ext="pickle"):
|
def save(item, file, ext=".pickle"):
|
||||||
file = "%s.%s" % (path.join(path.saria_dir(),file), ext)
|
file = "%s%s" % (path.join(path.saria_dir(),file), ext)
|
||||||
savefile(item, file)
|
savefile(item, file)
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,8 +18,9 @@ def savefile(item, file):
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def load(file, ext="pickle"):
|
def load(file, ext=".pickle"):
|
||||||
file = "%s.%s" % (path.join(path.saria_dir(),file),ext)
|
file = "%s%s" % (path.join(path.saria_dir(),file),ext)
|
||||||
|
print file
|
||||||
return loadfile(file)
|
return loadfile(file)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,16 @@
|
||||||
# Bryan Schumaker (8/7/2010)
|
# Bryan Schumaker (8/7/2010)
|
||||||
|
|
||||||
events = dict()
|
events = dict()
|
||||||
from threading import Thread
|
threads = None
|
||||||
|
|
||||||
|
start_thread = None
|
||||||
|
|
||||||
|
|
||||||
class EventThread(Thread):
|
def invite(key, func, bg=False):
|
||||||
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):
|
|
||||||
global events
|
global events
|
||||||
if key not in events:
|
if key not in events:
|
||||||
events[key] = set()
|
events[key] = set()
|
||||||
events[key].add(func)
|
events[key].add((func,bg))
|
||||||
|
|
||||||
|
|
||||||
def uninvite(key, func):
|
def uninvite(key, func):
|
||||||
|
@ -30,50 +20,43 @@ def uninvite(key, func):
|
||||||
events[key].discard(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
|
global events
|
||||||
if key not in events:
|
if key not in events:
|
||||||
return
|
return
|
||||||
for func in events[key]:
|
for func, bg in events[key]:
|
||||||
try:
|
try:
|
||||||
if arg == None:
|
if bg == False:
|
||||||
func()
|
func(*args)
|
||||||
else:
|
else:
|
||||||
func(arg)
|
start_thread(func, *args)
|
||||||
except Exception,e:
|
except Exception,e:
|
||||||
print
|
print
|
||||||
print "===== EVENT ERROR ====="
|
print "===== EVENT ERROR ====="
|
||||||
print "Event:", key
|
print "Event:", key
|
||||||
print "Function:", func
|
print "Function:", func
|
||||||
print "Args:", arg
|
print "Args:", args
|
||||||
print e
|
print e
|
||||||
print "The function has been uninvited from this event"
|
print "The function has been uninvited from this event"
|
||||||
print "====="
|
print "====="
|
||||||
|
|
||||||
|
|
||||||
def start_thread(func, action, arg):
|
def call(action, func=None, *args):
|
||||||
try:
|
start("PRE%s" % action, *args)
|
||||||
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)
|
|
||||||
if func == None:
|
if func == None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if bg == True:
|
res = func(*args)
|
||||||
res = start_thread(func, action, arg)
|
start("POST%s" % action, res)
|
||||||
else:
|
|
||||||
if arg == None:
|
|
||||||
res = func()
|
|
||||||
else:
|
|
||||||
res = func(arg)
|
|
||||||
start("POST%s" % action, res)
|
|
||||||
return 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
|
#!/usr/bin/python
|
||||||
# Bryan Schumaker (8/13/2010)
|
# Bryan Schumaker (8/13/2010)
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
now = datetime.datetime.now
|
||||||
|
before = now()
|
||||||
|
|
||||||
import libsaria
|
import libsaria
|
||||||
import ocarina
|
import ocarina
|
||||||
|
|
||||||
|
from ocarina import collection
|
||||||
|
|
||||||
libsaria.init()
|
libsaria.init()
|
||||||
libsaria.init_pref("window_size", (800,600))
|
libsaria.init_pref("window_size", (800,600))
|
||||||
|
|
||||||
prefs = libsaria.prefs
|
prefs = libsaria.prefs
|
||||||
|
|
||||||
import gtk
|
|
||||||
label = gtk.Label("Ocarina 4.1")
|
|
||||||
label.show()
|
|
||||||
|
|
||||||
win = ocarina.get_window(prefs["window_size"])
|
win = ocarina.get_window(prefs["window_size"])
|
||||||
ocarina.set_window_title("Ocarina 4.1")
|
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()
|
ocarina.startup()
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
# Bryan Schumaker (8/13/2010)
|
# Bryan Schumaker (8/13/2010)
|
||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
|
import gobject
|
||||||
import libsaria
|
import libsaria
|
||||||
|
|
||||||
|
gobject.threads_init()
|
||||||
|
|
||||||
# Lazy loaded modules
|
# Lazy loaded modules
|
||||||
window = None
|
window = None
|
||||||
tabs = 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)
|
# Bryan Schumaker (8/14/2010)
|
||||||
|
|
||||||
import ocarina
|
import ocarina
|
||||||
|
|
||||||
music = ocarina.libsaria.music
|
music = ocarina.libsaria.music
|
||||||
gtk = ocarina.gtk
|
gtk = ocarina.gtk
|
||||||
webkit = None
|
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