# ---------------------------------------------------------------------------- # Convert input file trs = Transcription(name=trs_input.GetName()+"-IPA") for n in args.n.split(','): print(" -> Tier {:s}:".format(n)) tier = trs_input.Find(n, case_sensitive=False) if tier is not None: new_tier = mapping.map_tier(tier) new_tier.SetName(n+"-IPA") new_tier.metadata = tier.metadata trs.Append(new_tier) else: print(" [IGNORED] Wrong tier name.") # Set the other members trs.metadata = trs_input.metadata # ---------------------------------------------------------------------------- # Write converted tiers if trs.GetSize() == 0: print("No tier converted. No file created.") sys.exit(1) infile, inext = os.path.splitext(args.i) filename = infile + "-ipa" + inext aio.write(filename, trs) print("File {:s} created.".format(filename))
class TrsList(wx.Panel): """ :author: Brigitte Bigi :organization: Laboratoire Parole et Langage, Aix-en-Provence, France :contact: [email protected] :license: GPL, v3 :copyright: Copyright (C) 2011-2018 Brigitte Bigi :summary: Show data about transcriptions, in a panel including a list of tiers. """ def __init__(self, parent, filename, trs=None, multiple=False): wx.Panel.__init__(self, parent, -1, size=wx.DefaultSize) # initialize the GUI self._prefs = Preferences() self._filename = filename self._dirty = False # the transcription was changed self._selected = False # the transcription is selected self._protected = [ ] # list of the tiers that are protected (can't be modified) if len(filename) == 0: self._filename = "Empty" boxtitle = self._create_title() self.tier_list = self._create_list(multiple) # load the Transcription if trs is None and len(filename) != 0: self.LoadFile(filename) else: self._transcription = trs # add Transcription information in the list for i in range(self._transcription.GetSize()): self.SetTierProperties(i) self._checksize() # events self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnListItemSelected, self.tier_list) self.Bind(wx.EVT_LIST_COL_CLICK, self.OnListItemSelected, self.tier_list) # layout sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(boxtitle, 0, wx.EXPAND | wx.ALL, border=4) sizer.Add(self.tier_list, 1, wx.EXPAND | wx.ALL, border=4) self.SetFont(self._prefs.GetValue('M_FONT')) self.SetForegroundColour(self._prefs.GetValue('M_FG_COLOUR')) self.SetBackgroundColour(self._prefs.GetValue('M_BG_COLOUR')) self._boxtitle.SetForegroundColour(FG_FILE_COLOUR) self.SetSizerAndFit(sizer) self.SetAutoLayout(True) self.Layout() # ---------------------------------------------------------------------- def _create_title(self): """ Create the title of the panel. """ _sizer = wx.BoxSizer(wx.HORIZONTAL) self._static_tx = wx.TextCtrl(self, -1, "File: ", style=wx.TE_READONLY | wx.NO_BORDER) self._boxtitle = wx.TextCtrl(self, -1, self._filename, style=wx.TE_READONLY | wx.NO_BORDER) _sizer.Add(self._static_tx, 0, wx.RIGHT, border=2) _sizer.Add(self._boxtitle, 1, wx.EXPAND) return _sizer # ---------------------------------------------------------------------- def _create_list(self, multiple=False): """ Create the list to show information of a each tier of a transcription. """ if multiple: tier_list = CheckListCtrl(self, -1, style=wx.LC_REPORT | wx.BORDER_NONE) else: tier_list = CheckListCtrl(self, -1, style=wx.LC_REPORT | wx.BORDER_NONE | wx.LC_SINGLE_SEL) # Add all columns col_names = [ " Nb ", " Name ", " Begin ", " End ", " Type ", " Size " ] for i, n in enumerate(col_names): tier_list.InsertColumn(i, n) # Fix column width for i in range(len(col_names)): tier_list.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) # Enlarge column with tier name tier_list.SetColumnWidth(1, 140) return tier_list # ------------------------------------------------------------------------- def SetTierProperties(self, tier_idx): """ Display tier properties. """ try: tier = self._transcription[tier_idx] if tier.IsPoint() is True: tier_type = "Point" elif tier.IsInterval(): tier_type = "Interval" elif tier.IsDisjoint(): tier_type = "Disjoint" else: tier_type = "Unknown" if tier.IsEmpty() is True: begin = " ... " end = " ... " else: begin = str(tier.GetBeginValue()) end = str(tier.GetEndValue()) self.tier_list.InsertStringItem(tier_idx, "Tier %d" % (tier_idx + 1)) self.tier_list.SetStringItem(tier_idx, 1, tier.GetName()) self.tier_list.SetStringItem(tier_idx, 2, begin) self.tier_list.SetStringItem(tier_idx, 3, end) self.tier_list.SetStringItem(tier_idx, 4, tier_type) self.tier_list.SetStringItem(tier_idx, 5, str(tier.GetSize())) except Exception as e: self.tier_list.InsertStringItem(1, "Error: " + str(e)) # ---------------------------------------------------------------------- # Callbacks... # ---------------------------------------------------------------------- def OnListItemSelected(self, event): """ An item of this panel was clicked. Inform the parent. """ evt = PanelSelectedEvent(panel=self) evt.SetEventObject(self) wx.PostEvent(self.GetParent(), evt) # ---------------------------------------------------------------------- # GUI # ---------------------------------------------------------------------- def SetPreferences(self, prefs): """ Set new preferences. """ self._prefs = prefs self.SetBackgroundColour(self._prefs.GetValue("M_BG_COLOUR")) self.SetForegroundColour(self._prefs.GetValue("M_FG_COLOUR")) self.SetFont(self._prefs.GetValue("M_FONT")) # ------------------------------------------------------------------------- def SetFont(self, font): """ Set a new font. """ wx.Window.SetFont(self, font) self.tier_list.SetFont(font) for i in range(self._transcription.GetSize()): self.tier_list.SetItemFont(i, font) self._static_tx.SetFont(font) self._boxtitle.SetFont(font) self.Layout() # bigger/smaller font can impact on the layout # ------------------------------------------------------------------------- def SetBackgroundColour(self, color): """ Set background. """ wx.Window.SetBackgroundColour(self, color) self.tier_list.SetBackgroundColour(color) for i in range(self._transcription.GetSize()): self.tier_list.SetItemBackgroundColour(i, color) self._static_tx.SetBackgroundColour(color) self._boxtitle.SetBackgroundColour(color) self.Refresh() # ------------------------------------------------------------------------- def SetForegroundColour(self, color): """ Set foreground and items text color. """ wx.Window.SetForegroundColour(self, color) self.tier_list.SetForegroundColour(color) for i in range(self._transcription.GetSize()): self.tier_list.SetItemTextColour(i, color) self._static_tx.SetForegroundColour(color) self.Refresh() # ---------------------------------------------------------------------- # Functions... # ---------------------------------------------------------------------- def Protect(self): """ Fix the current list of tiers as protected: they won't be changed. """ self._protected = [] for i, t in enumerate(self._transcription): self._protected.append(t) self.tier_list.SetItemTextColour(i, wx.Colour(140, 10, 10)) # ------------------------------------------------------------------------- def Unprotect(self): """ Erase the list of protected tiers. """ self._protected = [] # ---------------------------------------------------------------------- def IsSelected(self, tiername, case_sensitive=False): """ Return True if the tier is selected. """ i = self._transcription.GetIndex(tiername, case_sensitive) if i != -1: return self.tier_list.IsSelected(i) return False # ---------------------------------------------------------------------- def Select(self, tiername, case_sensitive=False): """ Select tiers which name is exactly matching. """ i = self._transcription.GetIndex(tiername, case_sensitive) if i != -1: self.tier_list.Select(i, on=True) return True return False # ---------------------------------------------------------------------- def Deselect(self): #for i in range(self.tier_list.GetItemCount()): # self.tier_list.Select(i, on=0) self.tier_list.DeSelectAll() # ---------------------------------------------------------------------- def Rename(self): """ Rename the selected tier. Dialog with the user to get the new name. """ if self._transcription.GetSize() == 0: return # Get the selected tier in the list sellist = self.tier_list.GetFirstSelected() # Nothing selected if sellist == -1: return # Too many selected items if self.tier_list.GetSelectedItemCount() > 1: ShowInformation(self, self._prefs, 'Only one tier has to be checked to be renamed...', style=wx.ICON_INFORMATION) return tier = self._transcription[sellist] if tier in self._protected: ShowInformation(self, self._prefs, "Attempt to rename a protected tier: forbidden!", style=wx.ICON_INFORMATION) return # Ask the user to enter a new name dlg = wx.TextEntryDialog(self, 'Indicate the new tier name', 'Data Roamer', 'Rename a tier.') dlg.SetValue(self._transcription[sellist].GetName()) if dlg.ShowModal() == wx.ID_OK: # Update tier name of the transcription tier.SetName(dlg.GetValue()) # Update tier name of the list self.tier_list.SetStringItem(sellist, 1, dlg.GetValue()) self._dirty = True self._boxtitle.SetForegroundColour(FG_FILE_DIRTY_COLOUR) self.Refresh() dlg.Destroy() # ---------------------------------------------------------------------- def Cut(self): """ Cut the selected tier. Return the clipboard. """ if self._transcription.GetSize() == 0: return # Get the selected tier in the list sellist = self.tier_list.GetFirstSelected() # No tier selected if sellist == -1: return # Too many selected items if self.tier_list.GetSelectedItemCount() > 1: ShowInformation(self, self._prefs, 'One tier must be checked.', style=wx.ICON_INFORMATION) return # Copy the tier to the clipboard tier = self._transcription[sellist] if tier in self._protected: ShowInformation(self, self._prefs, "Attempt to cut a protected tier: forbidden!", style=wx.ICON_INFORMATION) return clipboard = tier.Copy() # Delete tier of the transcription self._transcription.Remove(sellist) # Delete tier of the list self.tier_list.DeleteItem(sellist) # Update tier numbers of next items in the list. for i in range(sellist, self.tier_list.GetItemCount()): self.tier_list.SetStringItem(i, 0, "Tier " + str(i + 1)) self.Deselect() self._checksize() self._dirty = True self._boxtitle.SetForegroundColour(FG_FILE_DIRTY_COLOUR) self.Refresh() return clipboard # ---------------------------------------------------------------------- def Copy(self): """ Return the selected tier. """ if self._transcription.GetSize() == 0: return # Get the selected tier in the list sellist = self.tier_list.GetFirstSelected() if sellist == -1: return # Too many selected items if self.tier_list.GetSelectedItemCount() > 1: ShowInformation(self, self._prefs, "One tier must be checked", style=wx.ICON_INFORMATION) return # Copy the tier to the clipboard tier = self._transcription[sellist] return tier.Copy() # ---------------------------------------------------------------------- def Paste(self, clipboard): """ Paste the clipboard tier to the current page. """ # Get the clipboard tier if clipboard is None: return # Append clipboard to the transcription tier = clipboard #.Copy() self.Append(tier) # The tier comes from another Transcription... must update infos. if not (tier.GetTranscription() is self._transcription): # parent transcription tier.SetTranscription(self._transcription) # And if CtrlVocab... # TODO self._checksize() # ---------------------------------------------------------------------- def Delete(self): """ Delete the selected tier. Dialog with the user to confirm. """ if self._transcription.GetSize() == 0: return 0 # Get the selected tier in the list of this page sellist = self.tier_list.GetFirstSelected() if sellist == -1: return 0 # Get Indexes of tiers to remove indexes = [] while sellist != -1: indexes.append(sellist) sellist = self.tier_list.GetNextSelected(sellist) # Ask the user to confirm before deleting delete = 0 message = 'Are you sure you want to definitively delete:\n' \ '%d tiers in %s?' % (len(indexes), self._filename) dlg = ShowYesNoQuestion(self, self._prefs, message) if dlg == wx.ID_YES: for sellist in reversed(sorted(indexes)): item = self.tier_list.GetItem(sellist) tier = self._transcription[sellist] if tier in self._protected: pass else: # Delete tier of the transcription self._transcription.Remove(sellist) # Delete tier of the list self.tier_list.DeleteItem(sellist) delete = delete + 1 # Update tier numbers of next items in the list. for i in range(sellist, self.tier_list.GetItemCount()): self.tier_list.SetStringItem(i, 0, str(i + 1)) self._dirty = True self._boxtitle.SetForegroundColour(FG_FILE_DIRTY_COLOUR) self.Refresh self._checksize() return delete # ---------------------------------------------------------------------- def Duplicate(self): """ Duplicate the selected tier. """ if self._transcription.GetSize() == 0: return # Get the selected tier index in the list sellist = self.tier_list.GetFirstSelected() if sellist == -1: return # Too many selected items if self.tier_list.GetSelectedItemCount() > 1: ShowInformation(self, self._prefs, "One tier must be checked", style=wx.ICON_INFORMATION) return tier = self._transcription[sellist] self.Append(tier.Copy()) # ---------------------------------------------------------------------- def MoveUp(self): """ Move up the selected tier (except for the first one). """ if self._transcription.GetSize() == 0: return # Get the selected tier in the list sellist = self.tier_list.GetFirstSelected() if sellist == -1: return # Too many selected items if self.tier_list.GetSelectedItemCount() > 1: ShowInformation(self, self._prefs, "One tier must be checked", style=wx.ICON_INFORMATION) return # tier = self._transcription[sellist] if tier in self._protected: ShowInformation(self, self._prefs, "Attempt to move a protected tier: forbidden!", style=wx.ICON_INFORMATION) return #Impossible to move up the first tier. if sellist == 0: return # Pop selected tier from transcription. try: self._transcription._hierarchy.remove_tier( self._transcription[sellist] ) # waiting a better way to work with hierarchy... except Exception: pass self._transcription.Pop(sellist) # Delete old tier of the list self.tier_list.DeleteItem(sellist) # Add tier to the transcription tierindex = self._transcription.Add(tier, sellist - 1) # Add tier to the list self.SetTierProperties(tierindex) # Update tier number self.tier_list.SetStringItem(sellist, 0, str(sellist + 1)) # Let the item selected self.tier_list.Select(sellist - 1, on=True) self._dirty = True self._boxtitle.SetForegroundColour(FG_FILE_DIRTY_COLOUR) self.Refresh() # ---------------------------------------------------------------------- def MoveDown(self): """ Move down the selected tier (except for the last one). """ if self._transcription.GetSize() == 0: return # Get the selected tier in the list sellist = self.tier_list.GetFirstSelected() if sellist == -1: return # Too many selected items if self.tier_list.GetSelectedItemCount() > 1: ShowInformation(self, self._prefs, "One tier must be checked", style=wx.ICON_INFORMATION) return # tier = self._transcription[sellist] if tier in self._protected: ShowInformation(self, self._prefs, "Attempting to move a protected tier: forbidden!", style=wx.ICON_INFORMATION) return # Impossible to move down the last tier. if (sellist + 1) == self.tier_list.GetItemCount(): return # Pop selected tier from transcription. try: self._transcription._hierarchy.remove_tier( self._transcription[sellist] ) # waiting a better way to work with hierarchy... except Exception: pass self._transcription.Pop(sellist) # Delete old tier of the list self.tier_list.DeleteItem(sellist) # Add tier to the transcription if (sellist + 1) >= self.tier_list.GetItemCount(): tierindex = self._transcription.Add(tier) else: tierindex = self._transcription.Add(tier, sellist + 1) # Add tier to the list self.SetTierProperties(tierindex) # Update tier number self.tier_list.SetStringItem(sellist, 0, "Tier " + str(sellist + 1)) self.tier_list.SetStringItem(sellist + 1, 0, "Tier " + str(tierindex + 1)) # Let the item selected self.tier_list.Select(sellist + 1, on=True) self._dirty = True self._boxtitle.SetForegroundColour(FG_FILE_DIRTY_COLOUR) self.Refresh() # ---------------------------------------------------------------------- def Radius(self): """ Fix a new radius value to all TimePoint instances of the selected tier. """ if self._transcription.GetSize() == 0: return # Get the selected tier in the list sellist = self.tier_list.GetFirstSelected() if sellist == -1: return # tier = self._transcription[sellist] if tier in self._protected: ShowInformation(self, self._prefs, "Attempt to modify a protected tier: forbidden!", style=wx.ICON_INFORMATION) return # Open a dialog to ask the new radius value radius = tier.GetBegin().GetRadius() dlg = RadiusChooser(self, self._prefs, radius) if dlg.ShowModal() == wx.ID_OK: # Get the value r = dlg.GetValue() try: r = float(r) if r > 1.0: raise ValueError('Radius must range 0-1.') except: logging.info('Radius cancelled (can not be applied: %f).' % r) return # Set the value while sellist != -1: tier.SetRadius(r) logging.debug('Radius fixed to %f' % r) sellist = self.tier_list.GetNextSelected(sellist) dlg.Destroy() # ---------------------------------------------------------------------- def Preview(self): """ Open a grid frame with the selected tier content. """ if self._transcription.GetSize() == 0: return # Get the selected tier in the list sellist = self.tier_list.GetFirstSelected() if sellist == -1: return # Too many selected items if self.tier_list.GetSelectedItemCount() > 1: ShowInformation(self, self._prefs, "One tier only must be checked", style=wx.ICON_INFORMATION) return tier = self._transcription[sellist] dlg = PreviewTierDialog(self, self._prefs, tiers=[tier]) dlg.Show() # ---------------------------------------------------------------------- def Append(self, newtier): """ Append a tier in the transcription and in the list. """ # Append tier to the transcription tierindex = self._transcription.Append(newtier) # Append tier to the list self.SetTierProperties(tierindex) # Display information self._dirty = True self._boxtitle.SetForegroundColour(FG_FILE_DIRTY_COLOUR) self.Refresh() # ---------------------------------------------------------------------- def LoadFile(self, filename): """ Load a file in memory and show it. @param filename is an annotated file. """ self._filename = filename if os.path.exists(filename) is False: self._transcription = Transcription("Empty") return try: self._transcription = sppas.src.annotationdata.aio.read(filename) self._dirty = False self._boxtitle.SetForegroundColour(FG_FILE_COLOUR) self.Refresh() except Exception as e: logging.info('Error loading file %s: %s' % (filename, str(e))) self._transcription = Transcription("IO-Error") #raise # ---------------------------------------------------------------------- def Save(self): """ Save the current page content. """ if self._dirty is False: return try: sppas.src.annotationdata.aio.write(self._filename, self._transcription) self._dirty = False self._boxtitle.SetForegroundColour(FG_FILE_COLOUR) self.Refresh() except Exception as e: # give information ShowInformation(self, self._prefs, 'File not saved: %s' % str(e), style=wx.ICON_ERROR) # ---------------------------------------------------------------------- def SaveAs(self, filename): """ Save the current page content with another file name. Keep everything un-changed in self. """ try: sppas.src.annotationdata.aio.write(filename, self._transcription) except Exception as e: # give information ShowInformation(self, self._prefs, 'File not saved: %s' % str(e), style=wx.ICON_ERROR) # ---------------------------------------------------------------------- def GetTranscription(self): """ Return the Transcription. """ return self._transcription # ---------------------------------------------------------------------- def GetTranscriptionName(self): """ Return the name of the transcription. """ return self._transcription.GetName() # ---------------------------------------------------------------------- # Private # ---------------------------------------------------------------------- def _checksize(self): """ Check the transcription size. Append an "empty line" if transcription is empty. Remove this empty line if transcription is not empty. Return True if something has changed. """ # Append an "empty" line in the ListCtrl if self._transcription.GetSize() == 0 and self.tier_list.GetItemCount( ) == 0: self.tier_list.InsertStringItem(0, " ... ") if self._transcription.GetName() == "IO-Error": self.tier_list.SetStringItem( 0, 1, " Error while reading this file ") else: self.tier_list.SetStringItem(0, 1, " Empty file: no tiers ") for i in range(2, 5): self.tier_list.SetStringItem(0, i, " ") return True # Remove the "empty" line of the ListCtrl if self._transcription.GetSize() < self.tier_list.GetItemCount(): self.tier_list.DeleteItem(self.tier_list.GetItemCount() - 1) return True return False
class IPUsTrs(object): """ :author: Brigitte Bigi :organization: Laboratoire Parole et Langage, Aix-en-Provence, France :contact: [email protected] :license: GPL, v3 :copyright: Copyright (C) 2011-2017 Brigitte Bigi :summary: An IPUs segmentation from an already annotated data file. """ def __init__(self, trs): """ Creates a new IPUsTrs instance. :param trs: (Transcription) Input transcription from which it's possible to extract IPUs. Expected tiers are: - first tier: the IPUs content [required] - second tier: the IPUs file names [optional] """ super(IPUsTrs, self).__init__() self._trsinput = Transcription() self._units = list() # List of the content of the units (if any) self._names = list() # List of file names for IPUs (if any) self.set_transcription(trs) # ------------------------------------------------------------------ def get_units(self): """ Return the list of the IPUs contents. """ return self._units # ------------------------------------------------------------------ def get_names(self): """ Return the list of file names for IPUs. """ return self._names # ------------------------------------------------------------------ # Manage Transcription # ------------------------------------------------------------------ def set_transcription(self, trs): """ Set a new Transcription. :param trs: (Transcription) Input transcription from which it's possible to extract IPUs. """ if trs is not None: self._trsinput = trs else: self._trsinput = Transcription() # ------------------------------------------------------------------ # Units search # ------------------------------------------------------------------ def extract_bounds(self): """ Return bound values. Bound values are boolean to know if we expect a silence at start or end of the given transcription. It is relevant only if the transcription was created from a non-aligned file. """ # False means that I DON'T know if there is a silence: # It does not mean that there IS NOT a silence. # However, True means that there is a silence, for sure! bound_start = False bound_end = False if len(self._trsinput) > 0: # Check tier tier = self._trsinput[0] if tier.GetSize() == 0: raise IOError('Got no utterances.') # Fix bounds if tier[0].GetLabel().IsSilence() is True: bound_start = True if tier[-1].GetLabel().IsSilence() is True and tier.GetSize() > 1: bound_end = True return bound_start, bound_end # ------------------------------------------------------------------ def extract(self): """ Extract units and (if any) extract tracks and silences. :returns: tracks and silences, with time as seconds. """ self._units = list() self._names = list() if self._trsinput.GetSize() == 0: return [], [] trstier = self._trsinput[0] nametier = None if self._trsinput.GetSize() == 2: nametier = self._trsinput[1] tracks = [] silences = [] if trstier.GetSize() == 0: raise IOError('Got no utterances.') if trstier[0].GetLocation().GetValue().IsTimeInterval(): (tracks, silences) = self.extract_aligned(trstier, nametier) else: self.extract_units() return tracks, silences # ------------------------------------------------------------------ def extract_units(self): """ Extract IPUs content from a non-aligned transcription file. """ self._units = [] self._names = [] tier = self._trsinput[0] if tier.GetSize() == 0: raise IOError('Got no utterances.') i = 0 for ann in tier: if ann.GetLabel().IsSilence() is False: self._units.append(ann.GetLabel().GetValue()) self._names.append("track_%.06d" % (i+1)) i += 1 # ------------------------------------------------------------------ def extract_aligned(self, trstier, nametier): """ Extract from a time-aligned transcription file. :returns: a tuple with tracks and silences lists """ trstracks = [] silences = [] self._units = list() self._names = list() i = 0 last = trstier.GetSize() while i < last: # Set the current annotation values ann = trstier[i] # Save information if ann.GetLabel().IsSilence(): start = ann.GetLocation().GetBegin().GetMidpoint() end = ann.GetLocation().GetEnd().GetMidpoint() # Verify next annotations (concatenate all silences between 2 tracks) if (i + 1) < last: nextann = trstier[i + 1] while (i + 1) < last and nextann.GetLabel().IsSilence(): end = nextann.GetLocation().GetEnd().GetMidpoint() i += 1 if (i + 1) < last: nextann = trstier[i + 1] silences.append([start, end]) else: start = ann.GetLocation().GetBegin().GetMidpoint() end = ann.GetLocation().GetEnd().GetMidpoint() trstracks.append([start, end]) self._units.append(ann.GetLabel().GetValue()) if nametier is not None: aname = nametier.Find(ann.GetLocation().GetBegin().GetMidpoint(), ann.GetLocation().GetEnd().GetMidpoint(), True) if len(aname) == 0: trstracks.pop() self._units.pop() else: sf = sppasFileUtils(aname[0].GetLabel().GetValue()) # We have to take care in case of duplicated names filename = sf.clear_whitespace() if len(filename) == 0: filename = "unnamed_track" new_name = filename idx = 2 while new_name in self._names: new_name = u"%s_%.06d" % (filename, idx) idx += 1 self._names.append(new_name) # Continue i += 1 return trstracks, silences