$URL$
"""
+import pygame
+from pygame.locals import K_RETURN
from pgu.gui import FileDialog
+import pgu.gui.basic as basic
+import pgu.gui.input as input
+import pgu.gui.button as button
+import pgu.gui.pguglobals as pguglobals
+import pgu.gui.table as table
+import pgu.gui.area as area
+from pgu.gui.const import *
+from pgu.gui.dialog import Dialog
+from pgu.gui.app import Desktop
+import types
+
import os
+import tempfile
+from xml.etree import ElementTree
+from minwii.musicxml import musicXml2Song
+
+INDEX_TXT = 'index.txt'
+PICTURE_ITEM_SIZE = 64
+
+def appEventFactory(app, dlg) :
+ # monkey patch de la méthode gestionnaire d'événements :
+ # l'ensemble du Desktop écoute les événements de la roulette de la souris
+ # et les redirige sur la liste déroulante.
+ def _appEvent(self, e) :
+
+ if dlg.list.vscrollbar:
+ if not hasattr(dlg.list.vscrollbar,'value'):
+ return False
+
+ if e.type == pygame.locals.MOUSEBUTTONDOWN:
+ if e.button == 4: #wheel up
+ dlg.list.vscrollbar._click(-1)
+ return True
+ elif e.button == 5: #wheel down
+ dlg.list.vscrollbar._click(1)
+ return True
+ return Desktop.event(self, e)
+
+ return types.MethodType(_appEvent, app)
class FileOpenDialog(FileDialog):
def __init__(self, path):
- FileDialog.__init__(self,
- title_txt="Ouvrir une chanson",
- button_txt="Ouvrir",
- path=path,
- )
+ cls1 = 'filedialog'
+ if not path: self.curdir = os.getcwd()
+ else: self.curdir = path
+ self.dir_img = basic.Image(
+ pguglobals.app.theme.get(cls1+'.folder', '', 'image'))
+ self.soundfile_img = basic.Image(
+ pguglobals.app.theme.get(cls1+'.soundfile', '', 'image'))
+ td_style = {'padding_left': 4,
+ 'padding_right': 4,
+ 'padding_top': 2,
+ 'padding_bottom': 2}
+ self.title = basic.Label("Ouvrir une chanson", cls="dialog.title.label")
+ self.body = table.Table()
+ self.list = area.List(width=880, height=375)
+ self.input_dir = input.Input()
+ self.input_file = input.Input()
+ self._current_sort = 'alpha'
+ self._list_dir_()
+ self.button_ok = button.Button("Ouvrir")
+ self.button_sort_alpha = button.Button("A-Z")
+ self.button_sort_alpha.connect(CLICK, self._set_current_sort_, 'alpha')
+ self.button_sort_num = button.Button("0-9")
+ self.button_sort_num.connect(CLICK, self._set_current_sort_, 'num')
+ self.body.tr()
+ self.body.td(basic.Label("Dossier"), style=td_style, align=-1)
+ self.body.td(self.input_dir, style=td_style)
+ self.body.td(self.button_sort_alpha)
+ self.body.td(self.button_sort_num)
+ self.body.tr()
+ self.body.td(self.list, colspan=4, style=td_style)
+ self.list.connect(CHANGE, self._item_select_changed_, None)
+ #self.list.connect(CLICK, self._check_dbl_click_, None)
+ self._last_time_click = pygame.time.get_ticks()
+ self.button_ok.connect(CLICK, self._button_okay_clicked_, None)
+ self.body.tr()
+ self.body.td(basic.Label("Fichier"), style=td_style, align=-1)
+ self.body.td(self.input_file, style=td_style)
+ self.body.td(self.button_ok, style=td_style, colspan=2)
+ self.value = None
+ Dialog.__init__(self, self.title, self.body)
+
+ # monkey patch
+ app = pguglobals.app
+ self.__regularEventMethod = app.event
+ app.event = appEventFactory(app, self)
+ def close(self, w=None) :
+ FileDialog.close(self, w)
+ # retrait du monkey patch
+ app = pguglobals.app
+ app.event = self.__regularEventMethod
+
+
def _list_dir_(self):
self.input_dir.value = self.curdir
self.input_dir.pos = len(self.curdir)
files = []
try:
for i in os.listdir(self.curdir):
+ if i.startswith('.') : continue
if os.path.isdir(os.path.join(self.curdir, i)): dirs.append(i)
else: files.append(i)
except:
- self.input_file.value = "Opps! no access"
- #if '..' not in dirs: dirs.append('..')
+ self.input_file.value = "Dossier innacessible !"
+
dirs.sort()
- dirs = ['..'] + dirs
+ dirs.insert(0, '..')
files.sort()
for i in dirs:
- if i.startswith('.') and i != '..' :
- continue
self.list.add(i, image=self.dir_img, value=i)
-
+
+ xmlFiles = []
for i in files:
- if i.startswith('.') or (not i.endswith('.xml')) :
+ if not i.endswith('.xml') :
continue
- self.list.add(i,value=i)
-
+ filepath = os.path.join(self.curdir, i)
+ xmlFiles.append(filepath)
+
+ if xmlFiles :
+ printableLines = self.getPrintableLines(xmlFiles)
+ for l in printableLines :
+ imgpath = os.path.splitext(os.path.join(self.curdir, l[1]))[0] + '.jpg'
+ if os.path.exists(imgpath) :
+ img = pygame.image.load(imgpath)
+ iw, ih = img.get_width(), img.get_height()
+ style = {}
+ if iw > ih :
+ style['width'] = PICTURE_ITEM_SIZE
+ style['height'] = int(round(PICTURE_ITEM_SIZE * float(ih) / iw))
+ else :
+ style['heigth'] = PICTURE_ITEM_SIZE
+ style['width'] = int(round(PICTURE_ITEM_SIZE * float(iw) / ih))
+
+ img = basic.Image(img, style=style)
+ else :
+ img = self.soundfile_img
+ self.list.add(l[0], value = l[1], image = img)
+
self.list.set_vertical_scroll(0)
+
+ def getPrintableLines(self, xmlFiles) :
+ index = self.getUpdatedIndex(xmlFiles)
+
+ printableLines = []
+ for l in index :
+ l = l.strip()
+ l = l.split('\t')
+ printableLines.append(('%s - %s / %s' % (l[2], l[3], l[4]), l[0]))
+
+ return printableLines
+
+
+ @staticmethod
+ def getSongTitle(file) :
+ it = ElementTree.iterparse(file, ['start', 'end'])
+ creditFound = False
+ title = os.path.basename(file)
+
+ for evt, el in it :
+ if el.tag == 'credit' :
+ creditFound = True
+ if el.tag == 'credit-words' and creditFound:
+ title = el.text
+ break
+ if el.tag == 'part-list' :
+ # au delà de ce tag : aucune chance de trouver un titre
+ break
+ return title
+
+ @staticmethod
+ def getSongMetadata(file) :
+ metadata = {}
+ metadata['title'] = FileOpenDialog.getSongTitle(file).encode('iso-8859-1')
+ metadata['mtime'] = str(os.stat(file).st_mtime)
+ metadata['file'] = os.path.basename(file)
+ song = musicXml2Song(file)
+ metadata['distinctNotes'] = len(song.distinctNotes)
+
+ histo = song.intervalsHistogram
+ coeffInter = reduce(lambda a, b : a + b,
+ [abs(k) * v for k, v in histo.items()])
+
+ totInter = reduce(lambda a, b: a+b, histo.values())
+ totInter = totInter - histo.get(0, 0)
+ difficulty = int(round(float(coeffInter) / totInter, 0))
+ metadata['difficulty'] = difficulty
+
+ return metadata
+
+ def getUpdatedIndex(self, xmlFiles) :
+ indexTxtPath = os.path.join(self.curdir, INDEX_TXT)
+ index = []
+
+ if not os.path.exists(indexTxtPath) :
+ musicXmlFound = False
+ tmp = tempfile.TemporaryFile(mode='r+')
+ for file in xmlFiles :
+ try :
+ metadata = FileOpenDialog.getSongMetadata(file)
+ musicXmlFound = True
+ except ValueError, e :
+ print e
+ if e.args and e.args[0] == 'not a musicxml file' :
+ continue
+
+ line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
+ index.append(line)
+ tmp.write(line)
+
+ if musicXmlFound :
+ tmp.seek(0)
+ indexFile = open(indexTxtPath, 'w')
+ indexFile.write(tmp.read())
+ indexFile.close()
+ tmp.close()
+ else :
+ indexedFiles = {}
+ indexTxt = open(indexTxtPath, 'r')
+
+ # check if index is up to date, and update entries if so.
+ for l in filter(None, indexTxt.readlines()) :
+ parts = l.split('\t')
+ fileBaseName, modificationTime = parts[0], parts[1]
+ filePath = os.path.join(self.curdir, fileBaseName)
+
+ if not os.path.exists(filePath) :
+ continue
+
+ indexedFiles[fileBaseName] = l
+ currentMtime = str(os.stat(filePath).st_mtime)
+
+ # check modification time missmatch
+ if currentMtime != modificationTime :
+ try :
+ metadata = FileOpenDialog.getSongMetadata(filePath)
+ musicXmlFound = True
+ except ValueError, e :
+ print e
+ if e.args and e.args[0] == 'not a musicxml file' :
+ continue
+
+ metadata = FileOpenDialog.getSongMetadata(filePath)
+ line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
+ indexedFiles[fileBaseName] = line
+
+ # check for new files.
+ for file in xmlFiles :
+ fileBaseName = os.path.basename(file)
+ if not indexedFiles.has_key(fileBaseName) :
+ try :
+ metadata = FileOpenDialog.getSongMetadata(filePath)
+ musicXmlFound = True
+ except ValueError, e :
+ print e
+ if e.args and e.args[0] == 'not a musicxml file' :
+ continue
+
+ metadata = FileOpenDialog.getSongMetadata(file)
+ line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
+ indexedFiles[fileBaseName] = line
+
+ # ok, the index is up to date !
+
+ index = indexedFiles.values()
+
+
+ if self._current_sort == 'alpha' :
+ def s(a, b) :
+ da = desacc(a.split('\t')[2]).lower()
+ db = desacc(b.split('\t')[2]).lower()
+ return cmp(da, db)
+
+ elif self._current_sort == 'num' :
+ def s(a, b) :
+ da = int(a.split('\t')[3])
+ db = int(b.split('\t')[3])
+ return cmp(da, db)
+ else :
+ s = cmp
+
+ index.sort(s)
+ return index
+
+ def _set_current_sort_(self, arg) :
+ self._current_sort = arg
+ self.list.clear()
+ self._list_dir_()
+
+ def _check_dbl_click_(self, arg) :
+ if pygame.time.get_ticks() - self._last_time_click < 300 :
+ self._button_okay_clicked_(None)
+ else :
+ self._last_time_click = pygame.time.get_ticks()
+
+ def event(self, e) :
+ FileDialog.event(self, e)
+
+ if e.type == CLICK and \
+ e.button == 1 and \
+ self.list.rect.collidepoint(e.pos) :
+ self._check_dbl_click_(e)
+
+ if e.type == KEYDOWN and e.key == K_RETURN :
+ self._button_okay_clicked_(None)
+
+
+# utils
+from unicodedata import decomposition
+from string import printable
+_printable = dict([(c, True) for c in printable])
+isPrintable = _printable.has_key
+
+def _recurseDecomposition(uc):
+ deco = decomposition(uc).split()
+ fullDeco = []
+ if deco :
+ while (deco) :
+ code = deco.pop()
+ if code.startswith('<') :
+ continue
+ c = unichr(int(code, 16))
+ subDeco = decomposition(c).split()
+ if subDeco :
+ deco.extend(subDeco)
+ else :
+ fullDeco.append(c)
+ fullDeco.reverse()
+ else :
+ fullDeco.append(uc)
+
+ fullDeco = u''.join(filter(lambda c : isPrintable(c), fullDeco))
+ return fullDeco
+def desacc(s, encoding='iso-8859-1') :
+ us = s.decode(encoding, 'ignore')
+ ret = []
+ for uc in us :
+ ret.append(_recurseDecomposition(uc))
+ return u''.join(ret)
\ No newline at end of file