def __init__(self, channel, winlen=0.01): """ Constructor. @param channel (Channel) The channel to work on. @param winlen (float) Window length to estimate the volume. """ BaseVolume.__init__(self) self.winlen = winlen # Remember current position pos = channel.tell() # Rewind to the beginning channel.rewind() # Constants nbframes = int(winlen * channel.get_framerate()) nbvols = int(channel.get_duration()/winlen) + 1 self.volumes = [0]*nbvols for i in range(nbvols): frames = channel.get_frames( nbframes ) a = AudioFrames( frames, channel.get_sampwidth(), 1) self.volumes[i] = a.rms() if self.volumes[-1] == 0: self.volumes.pop() # Returns to the position where we was before channel.seek(pos) self.rms = channel.rms()
def get_silence(self, p=0.250, v=150, s=0.): """ Estimates silences from an audio file. @deprecated @param p (float) Minimum silence duration in seconds @param v (int) Expected minimum volume (rms value) @param s (float) Shift delta duration in seconds @return a set of frames corresponding to silences. """ self.channel.seek(0) self.silence = [] # Once silence has been found, continue searching in this interval nbreadframes = int(self.volstats.get_winlen() * self.channel.get_framerate()) afterloop_frames = int(nbreadframes/2) #int((nbreadframes/2) * self.channel.get_framerate()) initpos = i = self.channel.tell() # This scans the file in steps of frames whether a section's volume # is lower than silence_cap, if it is it is written to silence. while i < self.channel.get_nframes(): curframe = self.channel.get_frames(nbreadframes) a = AudioFrames( curframe, self.channel.get_sampwidth(), 1) #volume = audioutils.get_rms(curframe, self.channel.get_sampwidth()) volume = a.rms() if volume < v: # Continue searching in smaller steps whether the silence is # longer than read frames but smaller than read frames * 2. while volume < v and self.channel.tell() < self.channel.get_nframes(): curframe = self.channel.get_frames(afterloop_frames) a = AudioFrames( curframe, self.channel.get_sampwidth(), 1) volume = a.rms() #volume = audioutils.get_rms(curframe, self.channel.get_sampwidth()) # If the last sequence of silence ends where the new one starts # it's a continuous range. if self.silence and self.silence[-1][1] == i: self.silence[-1][1] = self.channel.tell() else: # append if silence is long enough duree = self.channel.tell() - i nbmin = int( (p+s) * self.channel.get_framerate()) if duree > nbmin: # Adjust silence start-pos __startpos = i + ( s * self.channel.get_framerate() ) # Adjust silence end-pos __endpos = self.channel.tell() - ( s * self.channel.get_framerate() ) self.silence.append([__startpos, __endpos]) i = self.channel.tell() # Return the position in the file to where it was when we got it. self.channel.seek(initpos)
def remove_offset(self): """ Convert the channel by removing the offset in the channel. """ newchannel = Channel() newchannel.sampwidth = self.sampwidth newchannel.framerate = self.framerate a = AudioFrames( self.channel.get_frames(self.channel.get_nframes()), self.channel.get_sampwidth(), 1) avg = a.avg() newchannel.set_frames( a.bias( - avg ) ) self.channel = newchannel
def rms(self): """ Return the root mean square of the whole file @return the root mean square of the audio file """ pos = self.tell() self.seek(0) a = AudioFrames(self.read_frames(self.get_nframes()), self.get_sampwidth(), self.get_nchannels()) self.seek(pos) return a.rms()
def clipping_rate(self, factor): """ Return the clipping rate of the frames @param factor (float) An interval to be more precise on clipping rate. It will consider that all frames outside the interval are clipped. Factor has to be between 0 and 1. """ pos = self.tell() self.seek(0) a = AudioFrames(self.read_frames(self.get_nframes()), self.get_sampwidth()) self.seek(pos) return a.clipping_rate(factor)
def bias(self, biasvalue): """ Convert the channel with a bias added to each frame. Samples wrap around in case of overflow. @param biasvalue (int) the value to bias the frames """ if biasvalue == 0: return newchannel = Channel() newchannel.sampwidth = self.sampwidth newchannel.framerate = self.framerate a = AudioFrames( self.channel.get_frames(self.channel.get_nframes()), self.channel.get_sampwidth(), 1) newchannel.set_frames( a.bias( biasvalue ) ) self.channel = newchannel
def mul(self, factor): """ Convert the channel that has all frames in the original channel are multiplied by the floating-point value factor. Samples are truncated in case of overflow. @param factor (float) the factor to multiply the frames """ if factor == 1.: return newchannel = Channel() newchannel.sampwidth = self.sampwidth newchannel.framerate = self.framerate a = AudioFrames( self.channel.get_frames(self.channel.get_nframes()), self.channel.get_sampwidth(), 1) newchannel.set_frames( a.mul(factor) ) self.channel = newchannel
def SetChannel(self, newchannel): """ Set a new channel, estimates the values to be displayed. """ # Set the channel self._channel = newchannel wx.BeginBusyCursor() b = wx.BusyInfo("Please wait while loading and analyzing data...") # To estimate values related to amplitude frames = self._channel.get_frames(self._channel.get_nframes()) self._ca = AudioFrames(frames, self._channel.get_sampwidth(), 1) # Estimates the RMS (=volume), then find where are silences, then IPUs self._cv = ChannelSilence(self._channel) self._cv.search_silences() # threshold=0, mintrackdur=0.08 self._cv.filter_silences() # minsildur=0.2 self._tracks = self._cv.extract_tracks() # mintrackdur=0.3 b.Destroy() b = None wx.EndBusyCursor()
print " - max: ", audiovol.max() print " - mean: ", round(audiovol.mean(),2) print " - median: ", round(audiovol.median(),2) print " - stdev: ", round(audiovol.stdev(),2) print " - coefvariation: ", round(audiovol.coefvariation(),2) else: for n in range(nc): print "Channel %d:"%(n) cidx = audio.extract_channel(n) channel = audio.get_channel(cidx) # Values related to amplitude frames = channel.get_frames(channel.get_nframes()) ca = AudioFrames(frames, channel.get_sampwidth(), 1) for i in range(2,9,2): f = float(i)/10. c = ca.clipping_rate( f ) * 100. print " - factor=%.1f: %.3f"%(f,c) # RMS (=volume) cv = ChannelVolume( channel ) print " Volume:" print " - min: ", cv.min() print " - max: ", cv.max() print " - mean: ", round(cv.mean(),2) print " - median: ", round(cv.median(),2) print " - stdev: ", round(cv.stdev(),2) print " - coefvariation: ", round(cv.coefvariation(),2)
class AudioRoamerPanel( wx.Panel ): """ @author: Brigitte Bigi @organization: Laboratoire Parole et Langage, Aix-en-Provence, France @contact: [email protected] @license: GPL, v3 @copyright: Copyright (C) 2011-2016 Brigitte Bigi @summary: Display info about a channel. Allows to save. This panel display all information about a channel: - amplitudes: nb of frames, min/max values, zero crossing, - clipping rates - volumes: min/max/mean - silence/speech automatic segmentation. Methods allow to save: - the channel or a fragment of the channel, in an audio file; - the information in a text file. """ FRAMERATES = [ "16000", "32000", "48000" ] SAMPWIDTH = [ "8", "16", "32" ] INFO_LABELS = {"framerate":(" Frame rate (Hz): ",FRAMERATES[0]), "sampwidth":(" Samp. width (bits): ",SAMPWIDTH[0]), "mul": (" Multiply values by: ","1.0"), "bias": (" Add bias value: ","0"), "offset": (" Remove offset value: ",False), "nframes": (" Number of frames: "," ... "), "minmax": (" Min/Max values: "," ... "), "cross": (" Zero crossings: "," ... "), "volmin": (" Volume min: "," ... "), "volmax": (" Volume max: "," ... "), "volavg": (" Volume mean: "," ... "), "volsil": (" Threshold volume: "," ... "), "nbipus": (" Number of IPUs: "," ... "), "duripus": (" Nb frames of IPUs: "," ... ") } def __init__(self, parent, preferences, channel): """ Create a new AudioRoamerPanel instance. @param parent (wxWindow) @param preferences (structs.Preferences) @param channel (audiodata.Channel) """ wx.Panel.__init__(self, parent) self._channel = channel # Channel self._filename = None # Fixed when "Save as" is clicked self._cv = None # ChannelSilence, fixed by ShowInfos self._tracks = None # the IPUs we found automatically self._ca = None # AudioFrames with only this channel, fixed by ShowInfos self._wxobj = {} # Dict of wx objects sizer = self._create_content() self.MODIFIABLES = {} for key in ["framerate","sampwidth","mul","bias","offset"]: self.MODIFIABLES[key] = AudioRoamerPanel.INFO_LABELS[key][1] self.SetPreferences(preferences) self.SetSizer(sizer) self.SetAutoLayout( True ) self.SetMinSize((MIN_PANEL_W,MIN_PANEL_H)) self.Layout() # ----------------------------------------------------------------------- # Private methods to show information about the channel into the GUI. # ----------------------------------------------------------------------- def _create_content(self): """ Create the main sizer, add content then return it. """ sizer = wx.BoxSizer(wx.HORIZONTAL) info = self._create_content_infos() clip = self._create_content_clipping() ipus = self._create_content_ipus() sizer.AddSpacer(5) sizer.Add(info, 1, wx.EXPAND, 0) sizer.AddSpacer(10) sizer.Add(clip, 0, wx.ALL, 0) sizer.AddSpacer(10) sizer.Add(ipus, 1, wx.EXPAND, 0) sizer.AddSpacer(5) return sizer def _create_content_infos(self): """ GUI design for amplitude and volume information. """ gbs = wx.GridBagSizer(10, 2) static_tx = wx.StaticText(self, -1, "Amplitude values: ") gbs.Add(static_tx, (0,0), (1,2), flag=wx.LEFT, border=2) self._wxobj["titleamplitude"] = (static_tx,None) self.__add_info(self, gbs, "nframes", 1) self.__add_info(self, gbs, "minmax", 2) self.__add_info(self, gbs, "cross", 3) static_tx = wx.StaticText(self, -1, "") gbs.Add(static_tx, (4,0), (1,2), flag=wx.LEFT, border=2) cfm = wx.ComboBox(self, -1, choices=AudioRoamerPanel.FRAMERATES, style=wx.CB_READONLY) cfm.SetMinSize((120,24)) self.__add_modifiable(self, gbs, cfm, "framerate", 5) self.Bind(wx.EVT_COMBOBOX, self.OnModif, cfm) csp = wx.ComboBox(self, -1, choices=AudioRoamerPanel.SAMPWIDTH, style=wx.CB_READONLY) csp.SetMinSize((120,24)) self.__add_modifiable(self, gbs, csp, "sampwidth", 6) self.Bind(wx.EVT_COMBOBOX, self.OnModif, csp) txm = wx.TextCtrl(self, -1, AudioRoamerPanel.INFO_LABELS["mul"][1], validator=TextAsNumericValidator()) txm.SetInsertionPoint(0) self.__add_modifiable(self, gbs, txm, "mul", 7) self.Bind(wx.EVT_TEXT_ENTER , self.OnModif, txm) txb = wx.TextCtrl(self, -1, AudioRoamerPanel.INFO_LABELS["bias"][1], validator=TextAsNumericValidator()) txb.SetInsertionPoint(0) self.__add_modifiable(self, gbs, txb, "bias", 8) self.Bind(wx.EVT_TEXT_ENTER, self.OnModif, txb) cb = wx.CheckBox(self, -1, style=wx.NO_BORDER) cb.SetValue( AudioRoamerPanel.INFO_LABELS["offset"][1] ) self.__add_modifiable(self, gbs, cb, "offset", 9) self.Bind(wx.EVT_CHECKBOX, self.OnModif, cb) gbs.AddGrowableCol(1) border = wx.BoxSizer() border.Add(gbs, 1, wx.ALL | wx.EXPAND, 10) return border def _create_content_clipping(self): """ GUI design for clipping information. """ gbs = wx.GridBagSizer(11, 2) static_tx = wx.StaticText(self, -1, "Clipping rates:") gbs.Add(static_tx, (0,0), (1,2), flag=wx.LEFT, border=2) self._wxobj["titleclipping"] = (static_tx,None) for i in range(1,10): self.__add_clip(self, gbs, i) border = wx.BoxSizer() border.Add(gbs, 1, wx.ALL | wx.EXPAND, 10) return border def _create_content_ipus(self): """ GUI design for information about an IPUs segmentation... """ gbs = wx.GridBagSizer(9, 2) static_tx = wx.StaticText(self, -1, "Root-mean square:") gbs.Add(static_tx, (0,0), (1,2), flag=wx.LEFT, border=2) self._wxobj["titlevolume"] = (static_tx,None) self.__add_info(self, gbs, "volmin", 1) self.__add_info(self, gbs, "volmax", 2) self.__add_info(self, gbs, "volavg", 3) static_tx = wx.StaticText(self, -1, "") gbs.Add(static_tx, (4, 0), (1,2), flag=wx.LEFT, border=2) static_tx = wx.StaticText(self, -1, "Automatic detection of silences:") gbs.Add(static_tx, (5, 0), (1,2), flag=wx.LEFT, border=2) self._wxobj["titleipus"] = (static_tx,None) self.__add_info(self, gbs, "volsil", 6) self.__add_info(self, gbs, "nbipus", 7) self.__add_info(self, gbs, "duripus", 8) border = wx.BoxSizer() border.Add(gbs, 1, wx.ALL | wx.EXPAND, 10) return border # ----------------------------------------------------------------------- # Callbacks to events # ----------------------------------------------------------------------- def OnModif(self, evt): """ Callback on a modifiable object: adapt foreground color. """ evtobj = evt.GetEventObject() evtvalue = evtobj.GetValue() for (key,defaultvalue) in self.MODIFIABLES.iteritems(): (tx,obj) = self._wxobj[key] if evtobj == obj: if evtvalue == defaultvalue: obj.SetForegroundColour( self._prefs.GetValue('M_FG_COLOUR') ) tx.SetForegroundColour( self._prefs.GetValue('M_FG_COLOUR') ) else: obj.SetForegroundColour( INFO_COLOUR ) tx.SetForegroundColour( INFO_COLOUR ) obj.Refresh() tx.Refresh() return # ----------------------------------------------------------------------- # Setters for GUI # ---------------------------------------------------------------------- def SetPreferences(self, prefs): """ Set new preferences. Refresh GUI. """ self._prefs = prefs self.SetFont( prefs.GetValue('M_FONT') ) self.SetBackgroundColour( prefs.GetValue('M_BG_COLOUR') ) self.SetForegroundColour( prefs.GetValue('M_FG_COLOUR') ) self.Refresh() # ---------------------------------------------------------------------- def SetFont(self, font): """ Change font of all wx texts. """ wx.Window.SetFont( self, font ) for (tx,obj) in self._wxobj.values(): tx.SetFont(font) if obj is not None: obj.SetFont(font) else: # a title (make it bold) newfont = wx.Font(font.GetPointSize(), font.GetFamily(), font.GetStyle(), wx.BOLD, False, font.GetFaceName(), font.GetEncoding()) tx.SetFont(newfont) # ---------------------------------------------------------------------- def SetBackgroundColour(self, color): """ Change background of all texts. """ wx.Window.SetBackgroundColour( self, color ) for (tx,obj) in self._wxobj.values(): tx.SetBackgroundColour( color ) if obj is not None: obj.SetBackgroundColour( color ) # ---------------------------------------------------------------------- def SetForegroundColour(self, color): """ Change foreground of all texts. """ wx.Window.SetForegroundColour( self, color ) for (tx,obj) in self._wxobj.values(): tx.SetForegroundColour( color ) if obj is not None: obj.SetForegroundColour( color ) # ---------------------------------------------------------------------- # Methods of the workers # ---------------------------------------------------------------------- def ShowInfo(self): """ Estimate all values then display the information. """ # we never estimated values. we have to do it! if self._cv is None: try: self.SetChannel(self._channel) except Exception as e: ShowInformation(self, self._prefs, "Error: %s"%str(e)) return # Amplitude self._wxobj["nframes"][1].ChangeValue( " "+str(self._channel.get_nframes())+" " ) self._wxobj["minmax"][1].ChangeValue( " "+str(self._ca.minmax())+" " ) self._wxobj["cross"][1].ChangeValue( " "+str(self._ca.cross())+" " ) # Modifiable fm = str(self._channel.get_framerate()) if not fm in AudioRoamerPanel.FRAMERATES: self._wxobj["framerate"][1].Append( fm ) self._wxobj["framerate"][1].SetStringSelection(fm) self.MODIFIABLES["framerate"] = fm sp = str(self._channel.get_sampwidth()*8) if not sp in AudioRoamerPanel.SAMPWIDTH: self._wxobj["sampwidth"][1].Append( sp ) self._wxobj["sampwidth"][1].SetStringSelection( sp ) self.MODIFIABLES["sampwidth"] = sp # Clipping for i in range(1,10): cr = self._ca.clipping_rate( float(i)/10. ) * 100. self._wxobj["clip"+str(i)][1].ChangeValue( " "+str( round(cr,2))+"% ") # Volumes / Silences vmin = self._cv.get_volstats().min() vmax = self._cv.get_volstats().max() vavg = self._cv.get_volstats().mean() self._wxobj["volmin"][1].ChangeValue( " "+str(vmin)+" ("+str(amp2db(vmin))+" dB) " ) self._wxobj["volmax"][1].ChangeValue( " "+str(vmax)+" ("+str(amp2db(vmax))+" dB) " ) self._wxobj["volavg"][1].ChangeValue( " "+str(int(vavg) )+" ("+str(amp2db(vavg))+" dB) ") self._wxobj["volsil"][1].ChangeValue( " "+str(self._cv.search_threshold_vol())+" " ) self._wxobj["nbipus"][1].ChangeValue( " "+str(len(self._tracks))+" " ) d = sum( [(e-s) for (s,e) in self._tracks] ) self._wxobj["duripus"][1].ChangeValue( " "+str(d)+" " ) # ----------------------------------------------------------------------- def SetChannel(self, newchannel): """ Set a new channel, estimates the values to be displayed. """ # Set the channel self._channel = newchannel wx.BeginBusyCursor() b = wx.BusyInfo("Please wait while loading and analyzing data...") # To estimate values related to amplitude frames = self._channel.get_frames(self._channel.get_nframes()) self._ca = AudioFrames(frames, self._channel.get_sampwidth(), 1) # Estimates the RMS (=volume), then find where are silences, then IPUs self._cv = ChannelSilence(self._channel) self._cv.search_silences() # threshold=0, mintrackdur=0.08 self._cv.filter_silences() # minsildur=0.2 self._tracks = self._cv.extract_tracks() # mintrackdur=0.3 b.Destroy() b = None wx.EndBusyCursor() # ----------------------------------------------------------------------- def ApplyChanges(self, from_time=None, to_time=None): """ Return a channel with changed applied. @param from_time (float) @param to_time (float) @return (Channel) new channel or None if nothing changed """ # Get the list of modifiable values from wx objects fm = int(self._wxobj["framerate"][1].GetValue()) sp = int(int(self._wxobj["sampwidth"][1].GetValue())/8) mul = float(self._wxobj["mul"][1].GetValue()) bias = int(self._wxobj["bias"][1].GetValue()) offset = self._wxobj["offset"][1].GetValue() dirty = False if from_time is None: from_frame = 0 else: from_frame = int( from_time * fm ) dirty = True if to_time is None: to_frame = self._channel.get_nframes() else: dirty = True to_frame = int(to_time * fm) channel = self._channel.extract_fragment(from_frame,to_frame) # If something changed, apply this/these change-s to the channel if fm != self._channel.get_framerate() or sp != self._channel.get_sampwidth() or mul != 1. or bias != 0 or offset is True: wx.BeginBusyCursor() b = wx.BusyInfo("Please wait while formatting data...") channelfmt = ChannelFormatter( channel ) channelfmt.set_framerate(fm) channelfmt.set_sampwidth(sp) channelfmt.convert() channelfmt.mul(mul) channelfmt.bias(bias) if offset is True: channelfmt.remove_offset() channel = channelfmt.get_channel() dirty = True b.Destroy() b = None wx.EndBusyCursor() if dirty is True: return channel return None # ----------------------------------------------------------------------- def SaveChannel(self, parentfilename, period=False): """ Save the channel in an audio file. @param parentfilename (str) @param period (bool) Save a portion of the channel only """ s = None e = None if period is True: dlg = PeriodChooser( self, self._prefs, 0., float(self._channel.get_nframes())/float(self._channel.get_framerate()) ) answer = dlg.ShowModal() if answer == wx.ID_OK: (s,e) = dlg.GetValues() try: s = float(s) e = float(e) if e < s: raise Exception except Exception: ShowInformation( self, self._prefs, "Error in the definition of the portion of time.", style=wx.ICON_ERROR) return dlg.Destroy() if answer != wx.ID_OK: return newfilename = SaveAsAudioFile() # If it is the OK response, process the data. if newfilename is not None: if newfilename == parentfilename: ShowInformation( self, self._prefs, "Assigning the current file name is forbidden. Choose a new file name.", style=wx.ICON_ERROR) return # Create a formatted channel try: channel = self.ApplyChanges(s,e) except Exception as e: ShowInformation( self, self._prefs, "Error while formatting the channel: %s"%str(e), style=wx.ICON_ERROR) return message = "File %s saved successfully."%newfilename if channel is None: channel = self._channel else: message +="\nYou can now open it with AudioRoamer to see your changes!" # Save the channel try: audio = AudioPCM() audio.append_channel(channel) audiodata.io.save(newfilename, audio) except Exception as e: message = "File not saved. Error: %s"%str(e) else: # Update members self._filename = newfilename ShowInformation( self, self._prefs, message, style=wx.ICON_ERROR) # ----------------------------------------------------------------------- def SaveInfos(self, parentfilename): """ Ask for a filename then save all displayed information. """ newfilename = SaveAsAnyFile() # If it is the OK response, process the data. if newfilename is not None: content = self._infos_content(parentfilename) with codecs.open(newfilename, "w", encoding) as fp: fp.write(content) # ----------------------------------------------------------------------- # Private methods to list information in a "formatted" text. # ----------------------------------------------------------------------- def _infos_content(self, parentfilename): content = "" content += self.__separator() content += self.__line(program + ' - Version ' + version) content += self.__line(copyright) content += self.__line("Web site: "+ url) content += self.__line("Contact: "+ author + "("+ contact + ")") content += self.__separator() content += self.__newline() content += self.__line("Date: " + str(datetime.datetime.now())) # General information content += self.__section("General information") content += self.__line("Channel filename: %s"%self._filename) content += self.__line("Channel extracted from file: "+parentfilename) content += self.__line("Duration: %s sec."%self._channel.get_duration()) content += self.__line("Framerate: %d Hz"%self._channel.get_framerate()) content += self.__line("Samp. width: %d bits"%(int(self._channel.get_sampwidth())*8)) # Amplitude content += self.__section("Amplitude") content += self.__line(AudioRoamerPanel.INFO_LABELS["nframes"][0]+self._wxobj["nframes"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["minmax"][0]+self._wxobj["minmax"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["cross"][0]+self._wxobj["cross"][1].GetValue()) # Clipping content += self.__section("Amplitude clipping") for i in range(1,10): f = self._ca.clipping_rate( float(i)/10. ) * 100. content += self.__item(" factor "+str(float(i)/10.)+": "+str(round(f,2))+"%") # Volume content += self.__section("Root-mean square") content += self.__line(AudioRoamerPanel.INFO_LABELS["volmin"][0]+self._wxobj["volmin"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["volmax"][0]+self._wxobj["volmax"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["volavg"][0]+self._wxobj["volavg"][1].GetValue()) # IPUs content += self.__section("Inter-Pausal Units automatic segmentation") content += self.__line(AudioRoamerPanel.INFO_LABELS["volsil"][0]+self._wxobj["volsil"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["nbipus"][0]+self._wxobj["nbipus"][1].GetValue()) content += self.__line(AudioRoamerPanel.INFO_LABELS["duripus"][0]+self._wxobj["duripus"][1].GetValue()) content += self.__newline() content += self.__separator() return content # ----------------------------------------------------------------------- # Private methods. # ----------------------------------------------------------------------- def __add_info(self, parent, gbs, key, row): """ Private method to add an info into the GridBagSizer. """ static_tx = wx.StaticText(parent, -1, AudioRoamerPanel.INFO_LABELS[key][0]) gbs.Add(static_tx, (row, 0), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) tx = wx.TextCtrl(parent, -1, AudioRoamerPanel.INFO_LABELS[key][1], style=wx.TE_READONLY) tx.SetMinSize((120,24)) gbs.Add(tx, (row, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) self._wxobj[key] = (static_tx,tx) def __add_clip(self, parent, gbs, i): """ Private method to add a clipping value in a GridBagSizer. """ static_tx = wx.StaticText(parent, -1, " factor "+str( float(i)/10.)+": " ) gbs.Add(static_tx, (i, 0), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) tx = wx.TextCtrl(parent, -1, " ... ", style=wx.TE_READONLY|wx.TE_RIGHT) gbs.Add(tx, (i, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=2) self._wxobj["clip"+str(i)] = (static_tx,tx) def __add_modifiable(self, parent, gbs, obj, key, row): static_tx = wx.StaticText(parent, -1, AudioRoamerPanel.INFO_LABELS[key][0]) #static_tx = wx.TextCtrl(parent, -1, AudioRoamerPanel.INFO_LABELS[key][0], style=wx.TE_READONLY|wx.TE_LEFT|wx.NO_BORDER) gbs.Add(static_tx, (row, 0), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) gbs.Add(obj, (row, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=2) self._wxobj[key] = (static_tx,obj) # ----------------------------------------------------------------------- def __section(self, title): """ Private method to make to look like a title. """ text = self.__newline() text += self.__separator() text += self.__line(title) text += self.__separator() text += self.__newline() return text def __line(self, msg): """ Private method to make a text as a simple line. """ text = msg.strip() text += self.__newline() return text def __item(self, msg): """ Private method to make a text as a simple item. """ text = " - " text += self.__line(msg) return text def __newline(self): """ Private method to return a new empty line. """ if wx.Platform == '__WXMAC__' or wx.Platform == '__WXGTK__': return "\n" return "\r\n" def __separator(self): """ Private method to return a separator line. """ text = "-----------------------------------------------------------------" text += self.__newline() return text