1 # -*- coding: utf-8 -*-
3 Boîte de dialogue pour sélection des chansons.
10 from pygame
.locals import K_RETURN
11 from pgu
.gui
import FileDialog
12 import pgu
.gui
.basic
as basic
13 import pgu
.gui
.input as input
14 import pgu
.gui
.button
as button
15 import pgu
.gui
.pguglobals
as pguglobals
16 import pgu
.gui
.table
as table
17 import pgu
.gui
.area
as area
18 from pgu
.gui
.const
import *
19 from pgu
.gui
.dialog
import Dialog
23 from xml
.etree
import ElementTree
24 from minwii
.musicxml
import musicXml2Song
26 INDEX_TXT
= 'index.txt'
28 class FileOpenDialog(FileDialog
):
32 def __init__(self
, path
):
34 if not path
: self
.curdir
= os
.getcwd()
35 else: self
.curdir
= path
36 self
.dir_img
= basic
.Image(
37 pguglobals
.app
.theme
.get(cls1
+'.folder', '', 'image'))
38 td_style
= {'padding_left': 4,
42 self
.title
= basic
.Label("Ouvrir un chanson", cls
="dialog.title.label")
43 self
.body
= table
.Table()
44 self
.list = area
.List(width
=700, height
=250)
45 self
.input_dir
= input.Input()
46 self
.input_file
= input.Input()
47 self
._current
_sort
= 'alpha'
49 self
.button_ok
= button
.Button("Ouvrir")
50 self
.button_sort_alpha
= button
.Button("A-Z")
51 self
.button_sort_alpha
.connect(CLICK
, self
._set
_current
_sort
_, 'alpha')
52 self
.button_sort_num
= button
.Button("0-9")
53 self
.button_sort_num
.connect(CLICK
, self
._set
_current
_sort
_, 'num')
55 self
.body
.td(basic
.Label("Dossier"), style
=td_style
, align
=-1)
56 self
.body
.td(self
.input_dir
, style
=td_style
)
57 self
.body
.td(self
.button_sort_alpha
)
58 self
.body
.td(self
.button_sort_num
)
60 self
.body
.td(self
.list, colspan
=4, style
=td_style
)
61 self
.list.connect(CHANGE
, self
._item
_select
_changed
_, None)
62 #self.list.connect(CLICK, self._check_dbl_click_, None)
63 self
._last
_time
_click
= pygame
.time
.get_ticks()
64 self
.button_ok
.connect(CLICK
, self
._button
_okay
_clicked
_, None)
66 self
.body
.td(basic
.Label("Fichier"), style
=td_style
, align
=-1)
67 self
.body
.td(self
.input_file
, style
=td_style
)
68 self
.body
.td(self
.button_ok
, style
=td_style
, colspan
=2)
70 Dialog
.__init
__(self
, self
.title
, self
.body
)
72 # FileDialog.__init__(self,
73 # title_txt="Ouvrir une chanson",
74 # button_txt="Ouvrir",
77 # self.list.style.width = 700
78 # self.list.style.height = 250
81 self
.input_dir
.value
= self
.curdir
82 self
.input_dir
.pos
= len(self
.curdir
)
83 self
.input_dir
.vpos
= 0
87 for i
in os
.listdir(self
.curdir
):
88 if i
.startswith('.') : continue
89 if os
.path
.isdir(os
.path
.join(self
.curdir
, i
)): dirs
.append(i
)
92 self
.input_file
.value
= "Dossier innacessible !"
99 self
.list.add(i
, image
=self
.dir_img
, value
=i
)
103 if not i
.endswith('.xml') :
105 filepath
= os
.path
.join(self
.curdir
, i
)
106 xmlFiles
.append(filepath
)
107 # self.list.add(FileOpenDialog.getSongTitle(filepath), value=i)
110 printableLines
= self
.getPrintableLines(xmlFiles
)
111 for l
in printableLines
:
112 self
.list.add(l
[0], value
= l
[1])
114 self
.list.set_vertical_scroll(0)
116 def getPrintableLines(self
, xmlFiles
) :
117 index
= self
.getUpdatedIndex(xmlFiles
)
123 printableLines
.append(('%s - %s / %s' % (l
[2], l
[3], l
[4]), l
[0]))
125 return printableLines
129 def getSongTitle(file) :
130 it
= ElementTree
.iterparse(file, ['start', 'end'])
132 title
= os
.path
.basename(file)
135 if el
.tag
== 'credit' :
137 if el
.tag
== 'credit-words' and creditFound
:
140 if el
.tag
== 'part-list' :
141 # au delà de ce tag : aucune chance de trouver un titre
146 def getSongMetadata(file) :
148 metadata
['title'] = FileOpenDialog
.getSongTitle(file).encode('iso-8859-1')
149 metadata
['mtime'] = str(os
.stat(file).st_mtime
)
150 metadata
['file'] = os
.path
.basename(file)
151 song
= musicXml2Song(file)
152 metadata
['distinctNotes'] = len(song
.distinctNotes
)
154 histo
= song
.intervalsHistogram
155 coeffInter
= reduce(lambda a
, b
: a
+ b
,
156 [abs(k
) * v
for k
, v
in histo
.items()])
158 totInter
= reduce(lambda a
, b
: a
+b
, histo
.values())
159 totInter
= totInter
- histo
.get(0, 0)
160 difficulty
= int(round(float(coeffInter
) / totInter
, 0))
161 metadata
['difficulty'] = difficulty
165 def getUpdatedIndex(self
, xmlFiles
) :
166 indexTxtPath
= os
.path
.join(self
.curdir
, INDEX_TXT
)
169 if not os
.path
.exists(indexTxtPath
) :
170 musicXmlFound
= False
171 tmp
= tempfile
.TemporaryFile(mode
='r+')
172 for file in xmlFiles
:
174 metadata
= FileOpenDialog
.getSongMetadata(file)
176 except ValueError, e
:
178 if e
.args
and e
.args
[0] == 'not a musicxml file' :
181 line
= '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
187 indexFile
= open(indexTxtPath
, 'w')
188 indexFile
.write(tmp
.read())
193 indexTxt
= open(indexTxtPath
, 'r')
195 # check if index is up to date, and update entries if so.
196 for l
in filter(None, indexTxt
.readlines()) :
197 parts
= l
.split('\t')
198 fileBaseName
, modificationTime
= parts
[0], parts
[1]
199 filePath
= os
.path
.join(self
.curdir
, fileBaseName
)
201 if not os
.path
.exists(filePath
) :
204 indexedFiles
[fileBaseName
] = l
205 currentMtime
= str(os
.stat(filePath
).st_mtime
)
207 # check modification time missmatch
208 if currentMtime
!= modificationTime
:
210 metadata
= FileOpenDialog
.getSongMetadata(filePath
)
212 except ValueError, e
:
214 if e
.args
and e
.args
[0] == 'not a musicxml file' :
217 metadata
= FileOpenDialog
.getSongMetadata(filePath
)
218 line
= '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
219 indexedFiles
[fileBaseName
] = line
221 # check for new files.
222 for file in xmlFiles
:
223 fileBaseName
= os
.path
.basename(file)
224 if not indexedFiles
.has_key(fileBaseName
) :
226 metadata
= FileOpenDialog
.getSongMetadata(filePath
)
228 except ValueError, e
:
230 if e
.args
and e
.args
[0] == 'not a musicxml file' :
233 metadata
= FileOpenDialog
.getSongMetadata(file)
234 line
= '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
235 indexedFiles
[fileBaseName
] = line
237 # ok, the index is up to date !
239 index
= indexedFiles
.values()
242 if self
._current
_sort
== 'alpha' :
244 da
= desacc(a
.split('\t')[2]).lower()
245 db
= desacc(b
.split('\t')[2]).lower()
248 elif self
._current
_sort
== 'num' :
250 da
= int(a
.split('\t')[3])
251 db
= int(b
.split('\t')[3])
259 def _set_current_sort_(self
, arg
) :
260 self
._current
_sort
= arg
264 def _check_dbl_click_(self
, arg
) :
265 if pygame
.time
.get_ticks() - self
._last
_time
_click
< 300 :
266 self
._button
_okay
_clicked
_(None)
268 self
._last
_time
_click
= pygame
.time
.get_ticks()
271 FileDialog
.event(self
, e
)
273 if e
.type == CLICK
and \
275 self
.list.rect
.collidepoint(e
.pos
) :
276 self
._check
_dbl
_click
_(e
)
278 if e
.type == KEYDOWN
and e
.key
== K_RETURN
:
279 self
._button
_okay
_clicked
_(None)
283 from unicodedata
import decomposition
284 from string
import printable
285 _printable
= dict([(c
, True) for c
in printable
])
286 isPrintable
= _printable
.has_key
288 def _recurseDecomposition(uc
):
289 deco
= decomposition(uc
).split()
294 if code
.startswith('<') :
296 c
= unichr(int(code
, 16))
297 subDeco
= decomposition(c
).split()
306 fullDeco
= u
''.join(filter(lambda c
: isPrintable(c
), fullDeco
))
309 def desacc(s
, encoding
='iso-8859-1') :
310 us
= s
.decode(encoding
, 'ignore')
313 ret
.append(_recurseDecomposition(uc
))