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:
Bryan Schumaker 2010-08-19 23:02:30 -04:00
parent 66a8c5169e
commit 15cc5d22f0
14 changed files with 266 additions and 79 deletions

View File

@ -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():

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View 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

41
libsaria/threads.py Normal file
View File

@ -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

View File

@ -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()

View File

@ -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

49
ocarina/collection.py Normal file
View File

@ -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)

49
ocarina/list.py Normal file
View File

@ -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

View File

@ -1,6 +1,7 @@
# Bryan Schumaker (8/14/2010)
import ocarina
music = ocarina.libsaria.music
gtk = ocarina.gtk
webkit = None

26
tests/threads.py Normal file
View File

@ -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()

18
tests/treewalk.py Normal file
View File

@ -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