119a2f13ea51b11242c07b6b7d0ab823b018008a
[minwii.git] / src / minwii / widgets / songfilebrowser.py
1 # -*- coding: utf-8 -*-
2 """
3 Boîte de dialogue pour sélection des chansons.
4
5 $Id$
6 $URL$
7 """
8
9 from pgu.gui import FileDialog
10 import pgu.gui.basic as basic
11 import pgu.gui.input as input
12 import pgu.gui.button as button
13 import pgu.gui.pguglobals as pguglobals
14 import pgu.gui.table as table
15 import pgu.gui.area as area
16 from pgu.gui.const import *
17 from pgu.gui.dialog import Dialog
18
19 import os
20 import tempfile
21 from xml.etree import ElementTree
22 from minwii.musicxml import musicXml2Song
23
24 INDEX_TXT = 'index.txt'
25
26 class FileOpenDialog(FileDialog):
27
28
29
30 def __init__(self, path):
31 cls1 = 'filedialog'
32 if not path: self.curdir = os.getcwd()
33 else: self.curdir = path
34 self.dir_img = basic.Image(
35 pguglobals.app.theme.get(cls1+'.folder', '', 'image'))
36 td_style = {'padding_left': 4,
37 'padding_right': 4,
38 'padding_top': 2,
39 'padding_bottom': 2}
40 self.title = basic.Label("Ouvrir un chanson", cls="dialog.title.label")
41 self.body = table.Table()
42 self.list = area.List(width=700, height=250)
43 self.input_dir = input.Input()
44 self.input_file = input.Input()
45 self._list_dir_()
46 self.button_ok = button.Button("Ouvrir")
47 self.button_sort_alpha = button.Button("A-Z")
48 self.button_sort_num = button.Button("0-9")
49 self.body.tr()
50 self.body.td(basic.Label("Dossier"), style=td_style, align=-1)
51 self.body.td(self.input_dir, style=td_style)
52 self.body.td(self.button_sort_alpha)
53 self.body.td(self.button_sort_num)
54 self.body.tr()
55 self.body.td(self.list, colspan=4, style=td_style)
56 self.list.connect(CHANGE, self._item_select_changed_, None)
57 self.button_ok.connect(CLICK, self._button_okay_clicked_, None)
58 self.body.tr()
59 self.body.td(basic.Label("Fichier"), style=td_style, align=-1)
60 self.body.td(self.input_file, style=td_style)
61 self.body.td(self.button_ok, style=td_style, colspan=2)
62 self.value = None
63 Dialog.__init__(self, self.title, self.body)
64
65 # FileDialog.__init__(self,
66 # title_txt="Ouvrir une chanson",
67 # button_txt="Ouvrir",
68 # path=path,
69 # )
70 # self.list.style.width = 700
71 # self.list.style.height = 250
72
73 def _list_dir_(self):
74 self.input_dir.value = self.curdir
75 self.input_dir.pos = len(self.curdir)
76 self.input_dir.vpos = 0
77 dirs = []
78 files = []
79 try:
80 for i in os.listdir(self.curdir):
81 if i.startswith('.') : continue
82 if os.path.isdir(os.path.join(self.curdir, i)): dirs.append(i)
83 else: files.append(i)
84 except:
85 self.input_file.value = "Dossier innacessible !"
86
87 dirs.sort()
88 dirs.insert(0, '..')
89
90 files.sort()
91 for i in dirs:
92 self.list.add(i, image=self.dir_img, value=i)
93
94 xmlFiles = []
95 for i in files:
96 if not i.endswith('.xml') :
97 continue
98 filepath = os.path.join(self.curdir, i)
99 xmlFiles.append(filepath)
100 # self.list.add(FileOpenDialog.getSongTitle(filepath), value=i)
101
102 if xmlFiles :
103 printableLines = self.getPrintableLines(xmlFiles)
104 for l in printableLines :
105 self.list.add(l[0], value = l[1])
106
107 self.list.set_vertical_scroll(0)
108
109 def getPrintableLines(self, xmlFiles) :
110 index = self.getUpdatedIndex(xmlFiles)
111
112 printableLines = []
113 for l in index :
114 l = l.strip()
115 l = l.split('\t')
116 printableLines.append(('%s - %s / %s' % (l[2], l[3], l[4]), l[0]))
117
118 return printableLines
119
120
121 @staticmethod
122 def getSongTitle(file) :
123 it = ElementTree.iterparse(file, ['start', 'end'])
124 creditFound = False
125 title = os.path.basename(file)
126
127 for evt, el in it :
128 if el.tag == 'credit' :
129 creditFound = True
130 if el.tag == 'credit-words' and creditFound:
131 title = el.text
132 break
133 if el.tag == 'part-list' :
134 # au delà de ce tag : aucune chance de trouver un titre
135 break
136 return title
137
138 @staticmethod
139 def getSongMetadata(file) :
140 metadata = {}
141 metadata['title'] = FileOpenDialog.getSongTitle(file).encode('iso-8859-1')
142 metadata['mtime'] = str(os.stat(file).st_mtime)
143 metadata['file'] = os.path.basename(file)
144 song = musicXml2Song(file)
145 metadata['distinctNotes'] = len(song.distinctNotes)
146
147 histo = song.intervalsHistogram
148 coeffInter = reduce(lambda a, b : a + b,
149 [abs(k) * v for k, v in histo.items()])
150
151 totInter = reduce(lambda a, b: a+b, histo.values())
152 totInter = totInter - histo.get(0, 0)
153 difficulty = int(round(float(coeffInter) / totInter, 0))
154 metadata['difficulty'] = difficulty
155
156 return metadata
157
158 def getUpdatedIndex(self, xmlFiles) :
159 indexTxtPath = os.path.join(self.curdir, INDEX_TXT)
160 index = []
161
162 if not os.path.exists(indexTxtPath) :
163 musicXmlFound = False
164 tmp = tempfile.TemporaryFile(mode='r+')
165 for file in xmlFiles :
166 try :
167 metadata = FileOpenDialog.getSongMetadata(file)
168 musicXmlFound = True
169 except ValueError, e :
170 print e
171 if e.args and e.args[0] == 'not a musicxml file' :
172 continue
173
174 line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
175 index.append(line)
176 tmp.write(line)
177
178 if musicXmlFound :
179 tmp.seek(0)
180 indexFile = open(indexTxtPath, 'w')
181 indexFile.write(tmp.read())
182 indexFile.close()
183 tmp.close()
184 else :
185 indexedFiles = {}
186 indexTxt = open(indexTxtPath, 'r')
187
188 # check if index is up to date, and update entries if so.
189 for l in filter(None, indexTxt.readlines()) :
190 parts = l.split('\t')
191 fileBaseName, modificationTime = parts[0], parts[1]
192 filePath = os.path.join(self.curdir, fileBaseName)
193
194 if not os.path.exists(filePath) :
195 continue
196
197 indexedFiles[fileBaseName] = l
198 currentMtime = str(os.stat(filePath).st_mtime)
199
200 # check modification time missmatch
201 if currentMtime != modificationTime :
202 try :
203 metadata = FileOpenDialog.getSongMetadata(filePath)
204 musicXmlFound = True
205 except ValueError, e :
206 print e
207 if e.args and e.args[0] == 'not a musicxml file' :
208 continue
209
210 metadata = FileOpenDialog.getSongMetadata(filePath)
211 line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
212 indexedFiles[fileBaseName] = line
213
214 # check for new files.
215 for file in xmlFiles :
216 fileBaseName = os.path.basename(file)
217 if not indexedFiles.has_key(fileBaseName) :
218 try :
219 metadata = FileOpenDialog.getSongMetadata(filePath)
220 musicXmlFound = True
221 except ValueError, e :
222 print e
223 if e.args and e.args[0] == 'not a musicxml file' :
224 continue
225
226 metadata = FileOpenDialog.getSongMetadata(file)
227 line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
228 indexedFiles[fileBaseName] = line
229
230 # ok, the index is up to date !
231
232 index = indexedFiles.values()
233 index.sort()
234
235
236 return index
237