33b5cd58f431b0d5becb33c877ef84a8e7b6ef8a
[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.body.tr()
48 self.body.td(basic.Label("Dossier"), style=td_style, align=-1)
49 self.body.td(self.input_dir, style=td_style)
50 self.body.tr()
51 self.body.td(self.list, colspan=3, style=td_style)
52 self.list.connect(CHANGE, self._item_select_changed_, None)
53 self.button_ok.connect(CLICK, self._button_okay_clicked_, None)
54 self.body.tr()
55 self.body.td(basic.Label("Fichier"), style=td_style, align=-1)
56 self.body.td(self.input_file, style=td_style)
57 self.body.td(self.button_ok, style=td_style)
58 self.value = None
59 Dialog.__init__(self, self.title, self.body)
60
61 # FileDialog.__init__(self,
62 # title_txt="Ouvrir une chanson",
63 # button_txt="Ouvrir",
64 # path=path,
65 # )
66 # self.list.style.width = 700
67 # self.list.style.height = 250
68
69 def _list_dir_(self):
70 self.input_dir.value = self.curdir
71 self.input_dir.pos = len(self.curdir)
72 self.input_dir.vpos = 0
73 dirs = []
74 files = []
75 try:
76 for i in os.listdir(self.curdir):
77 if i.startswith('.') : continue
78 if os.path.isdir(os.path.join(self.curdir, i)): dirs.append(i)
79 else: files.append(i)
80 except:
81 self.input_file.value = "Dossier innacessible !"
82
83 dirs.sort()
84 dirs.insert(0, '..')
85
86 files.sort()
87 for i in dirs:
88 self.list.add(i, image=self.dir_img, value=i)
89
90 xmlFiles = []
91 for i in files:
92 if not i.endswith('.xml') :
93 continue
94 filepath = os.path.join(self.curdir, i)
95 xmlFiles.append(filepath)
96 # self.list.add(FileOpenDialog.getSongTitle(filepath), value=i)
97
98 if xmlFiles :
99 printableLines = self.getPrintableLines(xmlFiles)
100 for l in printableLines :
101 self.list.add(l[0], value = l[1])
102
103 self.list.set_vertical_scroll(0)
104
105 def getPrintableLines(self, xmlFiles) :
106 index = self.getUpdatedIndex(xmlFiles)
107
108 printableLines = []
109 for l in index :
110 l = l.strip()
111 l = l.split('\t')
112 printableLines.append(('%s - %s / %s' % (l[2], l[3], l[4]), l[0]))
113
114 return printableLines
115
116
117 @staticmethod
118 def getSongTitle(file) :
119 it = ElementTree.iterparse(file, ['start', 'end'])
120 creditFound = False
121 title = os.path.basename(file)
122
123 for evt, el in it :
124 if el.tag == 'credit' :
125 creditFound = True
126 if el.tag == 'credit-words' and creditFound:
127 title = el.text
128 break
129 if el.tag == 'part-list' :
130 # au delà de ce tag : aucune chance de trouver un titre
131 break
132 return title
133
134 @staticmethod
135 def getSongMetadata(file) :
136 metadata = {}
137 metadata['title'] = FileOpenDialog.getSongTitle(file).encode('iso-8859-1')
138 metadata['mtime'] = str(os.stat(file).st_mtime)
139 metadata['file'] = os.path.basename(file)
140 song = musicXml2Song(file)
141 metadata['distinctNotes'] = len(song.distinctNotes)
142
143 histo = song.intervalsHistogram
144 coeffInter = reduce(lambda a, b : a + b,
145 [abs(k) * v for k, v in histo.items()])
146
147 totInter = reduce(lambda a, b: a+b, histo.values())
148 totInter = totInter - histo.get(0, 0)
149 difficulty = int(round(float(coeffInter) / totInter, 0))
150 metadata['difficulty'] = difficulty
151
152 return metadata
153
154 def getUpdatedIndex(self, xmlFiles) :
155 indexTxtPath = os.path.join(self.curdir, INDEX_TXT)
156 index = []
157
158 if not os.path.exists(indexTxtPath) :
159 musicXmlFound = False
160 tmp = tempfile.TemporaryFile(mode='r+')
161 for file in xmlFiles :
162 try :
163 metadata = FileOpenDialog.getSongMetadata(file)
164 musicXmlFound = True
165 except ValueError, e :
166 print e
167 if e.args and e.args[0] == 'not a musicxml file' :
168 continue
169
170 line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
171 index.append(line)
172 tmp.write(line)
173
174 if musicXmlFound :
175 tmp.seek(0)
176 indexFile = open(indexTxtPath, 'w')
177 indexFile.write(tmp.read())
178 indexFile.close()
179 tmp.close()
180 else :
181 indexedFiles = {}
182 indexTxt = open(indexTxtPath, 'r')
183
184 # check if index is up to date, and update entries if so.
185 for l in filter(None, indexTxt.readlines()) :
186 parts = l.split('\t')
187 fileBaseName, modificationTime = parts[0], parts[1]
188 filePath = os.path.join(self.curdir, fileBaseName)
189
190 if not os.path.exists(filePath) :
191 continue
192
193 indexedFiles[fileBaseName] = l
194 currentMtime = str(os.stat(filePath).st_mtime)
195
196 # check modification time missmatch
197 if currentMtime != modificationTime :
198 try :
199 metadata = FileOpenDialog.getSongMetadata(filePath)
200 musicXmlFound = True
201 except ValueError, e :
202 print e
203 if e.args and e.args[0] == 'not a musicxml file' :
204 continue
205
206 metadata = FileOpenDialog.getSongMetadata(filePath)
207 line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
208 indexedFiles[fileBaseName] = line
209
210 # check for new files.
211 for file in xmlFiles :
212 fileBaseName = os.path.basename(file)
213 if not indexedFiles.has_key(fileBaseName) :
214 try :
215 metadata = FileOpenDialog.getSongMetadata(filePath)
216 musicXmlFound = True
217 except ValueError, e :
218 print e
219 if e.args and e.args[0] == 'not a musicxml file' :
220 continue
221
222 metadata = FileOpenDialog.getSongMetadata(file)
223 line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
224 indexedFiles[fileBaseName] = line
225
226 # ok, the index is up to date !
227
228 index = indexedFiles.values()
229 index.sort()
230
231
232 return index
233