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'
27 PICTURE_ITEM_SIZE
= 64
29 class FileOpenDialog(FileDialog
):
33 def __init__(self
, path
):
35 if not path
: self
.curdir
= os
.getcwd()
36 else: self
.curdir
= path
37 self
.dir_img
= basic
.Image(
38 pguglobals
.app
.theme
.get(cls1
+'.folder', '', 'image'))
39 td_style
= {'padding_left': 4,
43 self
.title
= basic
.Label("Ouvrir un chanson", cls
="dialog.title.label")
44 self
.body
= table
.Table()
45 self
.list = area
.List(width
=700, height
=250)
46 self
.input_dir
= input.Input()
47 self
.input_file
= input.Input()
48 self
._current
_sort
= 'alpha'
50 self
.button_ok
= button
.Button("Ouvrir")
51 self
.button_sort_alpha
= button
.Button("A-Z")
52 self
.button_sort_alpha
.connect(CLICK
, self
._set
_current
_sort
_, 'alpha')
53 self
.button_sort_num
= button
.Button("0-9")
54 self
.button_sort_num
.connect(CLICK
, self
._set
_current
_sort
_, 'num')
56 self
.body
.td(basic
.Label("Dossier"), style
=td_style
, align
=-1)
57 self
.body
.td(self
.input_dir
, style
=td_style
)
58 self
.body
.td(self
.button_sort_alpha
)
59 self
.body
.td(self
.button_sort_num
)
61 self
.body
.td(self
.list, colspan
=4, style
=td_style
)
62 self
.list.connect(CHANGE
, self
._item
_select
_changed
_, None)
63 #self.list.connect(CLICK, self._check_dbl_click_, None)
64 self
._last
_time
_click
= pygame
.time
.get_ticks()
65 self
.button_ok
.connect(CLICK
, self
._button
_okay
_clicked
_, None)
67 self
.body
.td(basic
.Label("Fichier"), style
=td_style
, align
=-1)
68 self
.body
.td(self
.input_file
, style
=td_style
)
69 self
.body
.td(self
.button_ok
, style
=td_style
, colspan
=2)
71 Dialog
.__init
__(self
, self
.title
, self
.body
)
75 self
.input_dir
.value
= self
.curdir
76 self
.input_dir
.pos
= len(self
.curdir
)
77 self
.input_dir
.vpos
= 0
81 for i
in os
.listdir(self
.curdir
):
82 if i
.startswith('.') : continue
83 if os
.path
.isdir(os
.path
.join(self
.curdir
, i
)): dirs
.append(i
)
86 self
.input_file
.value
= "Dossier innacessible !"
93 self
.list.add(i
, image
=self
.dir_img
, value
=i
)
97 if not i
.endswith('.xml') :
99 filepath
= os
.path
.join(self
.curdir
, i
)
100 xmlFiles
.append(filepath
)
103 printableLines
= self
.getPrintableLines(xmlFiles
)
104 for l
in printableLines
:
105 imgpath
= os
.path
.splitext(os
.path
.join(self
.curdir
, l
[1]))[0] + '.jpg'
106 if os
.path
.exists(imgpath
) :
107 img
= pygame
.image
.load(imgpath
)
108 iw
, ih
= img
.get_width(), img
.get_height()
111 style
['width'] = PICTURE_ITEM_SIZE
112 style
['height'] = PICTURE_ITEM_SIZE
* float(ih
) / iw
114 style
['heigth'] = PICTURE_ITEM_SIZE
115 style
['width'] = PICTURE_ITEM_SIZE
* float(iw
) / ih
117 img
= basic
.Image(img
, style
=style
)
118 self
.list.add(l
[0], value
= l
[1], image
= img
)
120 self
.list.add(l
[0], value
= l
[1])
122 self
.list.set_vertical_scroll(0)
124 def getPrintableLines(self
, xmlFiles
) :
125 index
= self
.getUpdatedIndex(xmlFiles
)
131 printableLines
.append(('%s - %s / %s' % (l
[2], l
[3], l
[4]), l
[0]))
133 return printableLines
137 def getSongTitle(file) :
138 it
= ElementTree
.iterparse(file, ['start', 'end'])
140 title
= os
.path
.basename(file)
143 if el
.tag
== 'credit' :
145 if el
.tag
== 'credit-words' and creditFound
:
148 if el
.tag
== 'part-list' :
149 # au delà de ce tag : aucune chance de trouver un titre
154 def getSongMetadata(file) :
156 metadata
['title'] = FileOpenDialog
.getSongTitle(file).encode('iso-8859-1')
157 metadata
['mtime'] = str(os
.stat(file).st_mtime
)
158 metadata
['file'] = os
.path
.basename(file)
159 song
= musicXml2Song(file)
160 metadata
['distinctNotes'] = len(song
.distinctNotes
)
162 histo
= song
.intervalsHistogram
163 coeffInter
= reduce(lambda a
, b
: a
+ b
,
164 [abs(k
) * v
for k
, v
in histo
.items()])
166 totInter
= reduce(lambda a
, b
: a
+b
, histo
.values())
167 totInter
= totInter
- histo
.get(0, 0)
168 difficulty
= int(round(float(coeffInter
) / totInter
, 0))
169 metadata
['difficulty'] = difficulty
173 def getUpdatedIndex(self
, xmlFiles
) :
174 indexTxtPath
= os
.path
.join(self
.curdir
, INDEX_TXT
)
177 if not os
.path
.exists(indexTxtPath
) :
178 musicXmlFound
= False
179 tmp
= tempfile
.TemporaryFile(mode
='r+')
180 for file in xmlFiles
:
182 metadata
= FileOpenDialog
.getSongMetadata(file)
184 except ValueError, e
:
186 if e
.args
and e
.args
[0] == 'not a musicxml file' :
189 line
= '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
195 indexFile
= open(indexTxtPath
, 'w')
196 indexFile
.write(tmp
.read())
201 indexTxt
= open(indexTxtPath
, 'r')
203 # check if index is up to date, and update entries if so.
204 for l
in filter(None, indexTxt
.readlines()) :
205 parts
= l
.split('\t')
206 fileBaseName
, modificationTime
= parts
[0], parts
[1]
207 filePath
= os
.path
.join(self
.curdir
, fileBaseName
)
209 if not os
.path
.exists(filePath
) :
212 indexedFiles
[fileBaseName
] = l
213 currentMtime
= str(os
.stat(filePath
).st_mtime
)
215 # check modification time missmatch
216 if currentMtime
!= modificationTime
:
218 metadata
= FileOpenDialog
.getSongMetadata(filePath
)
220 except ValueError, e
:
222 if e
.args
and e
.args
[0] == 'not a musicxml file' :
225 metadata
= FileOpenDialog
.getSongMetadata(filePath
)
226 line
= '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
227 indexedFiles
[fileBaseName
] = line
229 # check for new files.
230 for file in xmlFiles
:
231 fileBaseName
= os
.path
.basename(file)
232 if not indexedFiles
.has_key(fileBaseName
) :
234 metadata
= FileOpenDialog
.getSongMetadata(filePath
)
236 except ValueError, e
:
238 if e
.args
and e
.args
[0] == 'not a musicxml file' :
241 metadata
= FileOpenDialog
.getSongMetadata(file)
242 line
= '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
243 indexedFiles
[fileBaseName
] = line
245 # ok, the index is up to date !
247 index
= indexedFiles
.values()
250 if self
._current
_sort
== 'alpha' :
252 da
= desacc(a
.split('\t')[2]).lower()
253 db
= desacc(b
.split('\t')[2]).lower()
256 elif self
._current
_sort
== 'num' :
258 da
= int(a
.split('\t')[3])
259 db
= int(b
.split('\t')[3])
267 def _set_current_sort_(self
, arg
) :
268 self
._current
_sort
= arg
272 def _check_dbl_click_(self
, arg
) :
273 if pygame
.time
.get_ticks() - self
._last
_time
_click
< 300 :
274 self
._button
_okay
_clicked
_(None)
276 self
._last
_time
_click
= pygame
.time
.get_ticks()
279 FileDialog
.event(self
, e
)
281 if e
.type == CLICK
and \
283 self
.list.rect
.collidepoint(e
.pos
) :
284 self
._check
_dbl
_click
_(e
)
286 if e
.type == KEYDOWN
and e
.key
== K_RETURN
:
287 self
._button
_okay
_clicked
_(None)
291 from unicodedata
import decomposition
292 from string
import printable
293 _printable
= dict([(c
, True) for c
in printable
])
294 isPrintable
= _printable
.has_key
296 def _recurseDecomposition(uc
):
297 deco
= decomposition(uc
).split()
302 if code
.startswith('<') :
304 c
= unichr(int(code
, 16))
305 subDeco
= decomposition(c
).split()
314 fullDeco
= u
''.join(filter(lambda c
: isPrintable(c
), fullDeco
))
317 def desacc(s
, encoding
='iso-8859-1') :
318 us
= s
.decode(encoding
, 'ignore')
321 ret
.append(_recurseDecomposition(uc
))