1 # -*- coding: utf-8 -*-
3 Boîte de dialogue pour sélection des chansons.
10 from pgu
.gui
import FileDialog
11 import pgu
.gui
.basic
as basic
12 import pgu
.gui
.input as input
13 import pgu
.gui
.button
as button
14 import pgu
.gui
.pguglobals
as pguglobals
15 import pgu
.gui
.table
as table
16 import pgu
.gui
.area
as area
17 from pgu
.gui
.const
import *
18 from pgu
.gui
.dialog
import Dialog
22 from xml
.etree
import ElementTree
23 from minwii
.musicxml
import musicXml2Song
25 INDEX_TXT
= 'index.txt'
27 class FileOpenDialog(FileDialog
):
31 def __init__(self
, path
):
33 if not path
: self
.curdir
= os
.getcwd()
34 else: self
.curdir
= path
35 self
.dir_img
= basic
.Image(
36 pguglobals
.app
.theme
.get(cls1
+'.folder', '', 'image'))
37 td_style
= {'padding_left': 4,
41 self
.title
= basic
.Label("Ouvrir un chanson", cls
="dialog.title.label")
42 self
.body
= table
.Table()
43 self
.list = area
.List(width
=700, height
=250)
44 self
.input_dir
= input.Input()
45 self
.input_file
= input.Input()
46 self
._current
_sort
= 'alpha'
48 self
.button_ok
= button
.Button("Ouvrir")
49 self
.button_sort_alpha
= button
.Button("A-Z")
50 self
.button_sort_alpha
.connect(CLICK
, self
._set
_current
_sort
_, 'alpha')
51 self
.button_sort_num
= button
.Button("0-9")
52 self
.button_sort_num
.connect(CLICK
, self
._set
_current
_sort
_, 'num')
54 self
.body
.td(basic
.Label("Dossier"), style
=td_style
, align
=-1)
55 self
.body
.td(self
.input_dir
, style
=td_style
)
56 self
.body
.td(self
.button_sort_alpha
)
57 self
.body
.td(self
.button_sort_num
)
59 self
.body
.td(self
.list, colspan
=4, style
=td_style
)
60 self
.list.connect(CHANGE
, self
._item
_select
_changed
_, None)
61 self
.list.connect(CLICK
, self
._check
_dbl
_click
_, None)
62 self
._last
_time
_click
= pygame
.time
.get_ticks()
63 self
.button_ok
.connect(CLICK
, self
._button
_okay
_clicked
_, None)
65 self
.body
.td(basic
.Label("Fichier"), style
=td_style
, align
=-1)
66 self
.body
.td(self
.input_file
, style
=td_style
)
67 self
.body
.td(self
.button_ok
, style
=td_style
, colspan
=2)
69 Dialog
.__init
__(self
, self
.title
, self
.body
)
71 # FileDialog.__init__(self,
72 # title_txt="Ouvrir une chanson",
73 # button_txt="Ouvrir",
76 # self.list.style.width = 700
77 # self.list.style.height = 250
80 self
.input_dir
.value
= self
.curdir
81 self
.input_dir
.pos
= len(self
.curdir
)
82 self
.input_dir
.vpos
= 0
86 for i
in os
.listdir(self
.curdir
):
87 if i
.startswith('.') : continue
88 if os
.path
.isdir(os
.path
.join(self
.curdir
, i
)): dirs
.append(i
)
91 self
.input_file
.value
= "Dossier innacessible !"
98 self
.list.add(i
, image
=self
.dir_img
, value
=i
)
102 if not i
.endswith('.xml') :
104 filepath
= os
.path
.join(self
.curdir
, i
)
105 xmlFiles
.append(filepath
)
106 # self.list.add(FileOpenDialog.getSongTitle(filepath), value=i)
109 printableLines
= self
.getPrintableLines(xmlFiles
)
110 for l
in printableLines
:
111 self
.list.add(l
[0], value
= l
[1])
113 self
.list.set_vertical_scroll(0)
115 def getPrintableLines(self
, xmlFiles
) :
116 index
= self
.getUpdatedIndex(xmlFiles
)
122 printableLines
.append(('%s - %s / %s' % (l
[2], l
[3], l
[4]), l
[0]))
124 return printableLines
128 def getSongTitle(file) :
129 it
= ElementTree
.iterparse(file, ['start', 'end'])
131 title
= os
.path
.basename(file)
134 if el
.tag
== 'credit' :
136 if el
.tag
== 'credit-words' and creditFound
:
139 if el
.tag
== 'part-list' :
140 # au delà de ce tag : aucune chance de trouver un titre
145 def getSongMetadata(file) :
147 metadata
['title'] = FileOpenDialog
.getSongTitle(file).encode('iso-8859-1')
148 metadata
['mtime'] = str(os
.stat(file).st_mtime
)
149 metadata
['file'] = os
.path
.basename(file)
150 song
= musicXml2Song(file)
151 metadata
['distinctNotes'] = len(song
.distinctNotes
)
153 histo
= song
.intervalsHistogram
154 coeffInter
= reduce(lambda a
, b
: a
+ b
,
155 [abs(k
) * v
for k
, v
in histo
.items()])
157 totInter
= reduce(lambda a
, b
: a
+b
, histo
.values())
158 totInter
= totInter
- histo
.get(0, 0)
159 difficulty
= int(round(float(coeffInter
) / totInter
, 0))
160 metadata
['difficulty'] = difficulty
164 def getUpdatedIndex(self
, xmlFiles
) :
165 indexTxtPath
= os
.path
.join(self
.curdir
, INDEX_TXT
)
168 if not os
.path
.exists(indexTxtPath
) :
169 musicXmlFound
= False
170 tmp
= tempfile
.TemporaryFile(mode
='r+')
171 for file in xmlFiles
:
173 metadata
= FileOpenDialog
.getSongMetadata(file)
175 except ValueError, e
:
177 if e
.args
and e
.args
[0] == 'not a musicxml file' :
180 line
= '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
186 indexFile
= open(indexTxtPath
, 'w')
187 indexFile
.write(tmp
.read())
192 indexTxt
= open(indexTxtPath
, 'r')
194 # check if index is up to date, and update entries if so.
195 for l
in filter(None, indexTxt
.readlines()) :
196 parts
= l
.split('\t')
197 fileBaseName
, modificationTime
= parts
[0], parts
[1]
198 filePath
= os
.path
.join(self
.curdir
, fileBaseName
)
200 if not os
.path
.exists(filePath
) :
203 indexedFiles
[fileBaseName
] = l
204 currentMtime
= str(os
.stat(filePath
).st_mtime
)
206 # check modification time missmatch
207 if currentMtime
!= modificationTime
:
209 metadata
= FileOpenDialog
.getSongMetadata(filePath
)
211 except ValueError, e
:
213 if e
.args
and e
.args
[0] == 'not a musicxml file' :
216 metadata
= FileOpenDialog
.getSongMetadata(filePath
)
217 line
= '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
218 indexedFiles
[fileBaseName
] = line
220 # check for new files.
221 for file in xmlFiles
:
222 fileBaseName
= os
.path
.basename(file)
223 if not indexedFiles
.has_key(fileBaseName
) :
225 metadata
= FileOpenDialog
.getSongMetadata(filePath
)
227 except ValueError, e
:
229 if e
.args
and e
.args
[0] == 'not a musicxml file' :
232 metadata
= FileOpenDialog
.getSongMetadata(file)
233 line
= '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
234 indexedFiles
[fileBaseName
] = line
236 # ok, the index is up to date !
238 index
= indexedFiles
.values()
241 if self
._current
_sort
== 'alpha' :
243 da
= desacc(a
.split('\t')[2]).lower()
244 db
= desacc(b
.split('\t')[2]).lower()
247 elif self
._current
_sort
== 'num' :
249 da
= int(a
.split('\t')[3])
250 db
= int(b
.split('\t')[3])
258 def _set_current_sort_(self
, arg
) :
259 self
._current
_sort
= arg
263 def _check_dbl_click_(self
, arg
) :
264 if pygame
.time
.get_ticks() - self
._last
_time
_click
< 300 :
265 self
._button
_okay
_clicked
_(None)
267 self
._last
_time
_click
= pygame
.time
.get_ticks()
271 from unicodedata
import decomposition
272 from string
import printable
273 _printable
= dict([(c
, True) for c
in printable
])
274 isPrintable
= _printable
.has_key
276 def _recurseDecomposition(uc
):
277 deco
= decomposition(uc
).split()
282 if code
.startswith('<') :
284 c
= unichr(int(code
, 16))
285 subDeco
= decomposition(c
).split()
294 fullDeco
= u
''.join(filter(lambda c
: isPrintable(c
), fullDeco
))
298 us
= s
.decode('utf-8', 'ignore')
301 ret
.append(_recurseDecomposition(uc
))