Commits

Anonymous committed ac94d92

Release v1.4 -- Context-dependent settings

* Context-dependent settings are now properly handled, making it
possible to save all parameters of Orange-Textable widgets.
* Widget Display now has a distinction between advanced and
basic settings.
* A new normalization mode has been added to the LTTL.Table class
and the Convert widget, namely "(independence) quotients".
* Fixed a bug in Text Files widget that resulted in data not
being always correctly sent to other widgets.
* Fixed a bug in LTTL.utils.py that prevented conditional import
of xml elements depending on their attribute values using
widget Extract XML; also changed this widget's default value
for option "Remove markup" to True.
* Enabled the selection of a larger number of subsamples in
widget Variety (up to 10'000).
* Fixed a bug that led to redundant calculations in most widgets.
* Fixed a bug that prevented widget Intersect to operate on
annotation values of the "source" segmentation.
* Added missing tooltips, corrected some existing ones, and
added more details in others; also slightly changed a few
widgets' description.
* Considerably expanded the online documentation (detailed
installation instruction are given for PC and MacOS X;
section Getting Started is now complete, including
subsection Annotations; widgets Text Field, Text Files, URLs,
and Preprocess have a documentation page accessible from
within the Canvas, via their instances' contextual menu).
* User Guide (in French) has been updated.

Comments (0)

Files changed (127)

 
 Documentation is found at:
 
-Readthedocs_
+http://orange-textable.readthedocs.org/
 
-.. _Readthedocs: https://orange-textable.readthedocs.org/
-
-Textable was designed and implemented by `LangTech Sarl <http://langtech.ch>`_ on behalf of the department of language and information sciences (SLI_) at the `University of Lausanne <http://www.unil.ch>`_.
+Textable was designed and implemented by `LangTech Sarl <http://langtech.ch>`_ on behalf of the
+department of language and information sciences (SLI_) at the `University of Lausanne <http://www.unil.ch>`_.
 
 .. _SLI: http://www.unil.ch/sli
 

_textable/widgets/LTTL/Address.py

 #=============================================================================
 # Class LTTL.Address, v0.05
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the LTTL package v1.3
+# This file is part of the LTTL package v1.4
 #
-# LTTL v1.3 is free software: you can redistribute it and/or modify
+# LTTL v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# LTTL v1.3 is distributed in the hope that it will be useful,
+# LTTL v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with LTTL v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with LTTL v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 class Address():

_textable/widgets/LTTL/Input.py

 #=============================================================================
 # Class LTTL.Input, v0.09
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the LTTL package v1.3
+# This file is part of the LTTL package v1.4
 #
-# LTTL v1.3 is free software: you can redistribute it and/or modify
+# LTTL v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# LTTL v1.3 is distributed in the hope that it will be useful,
+# LTTL v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with LTTL v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with LTTL v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 from Segment        import Segment

_textable/widgets/LTTL/Processor.py

 #=============================================================================
 # Class LTTL.Processor, v0.14
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the LTTL package v1.3
+# This file is part of the LTTL package v1.4
 #
-# LTTL v1.3 is free software: you can redistribute it and/or modify
+# LTTL v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# LTTL v1.3 is distributed in the hope that it will be useful,
+# LTTL v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with LTTL v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with LTTL v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 from __future__   import division

_textable/widgets/LTTL/Recoder.py

 #=============================================================================
 # Class LTTL.Recoder, v0.05
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the LTTL package v1.3
+# This file is part of the LTTL package v1.4
 #
-# LTTL v1.3 is free software: you can redistribute it and/or modify
+# LTTL v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# LTTL v1.3 is distributed in the hope that it will be useful,
+# LTTL v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with LTTL v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with LTTL v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 import unicodedata, re

_textable/widgets/LTTL/Segment.py

 #=============================================================================
 # Class LTTL.Segment, v0.13
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the LTTL package v1.3
+# This file is part of the LTTL package v1.4
 #
-# LTTL v1.3 is free software: you can redistribute it and/or modify
+# LTTL v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# LTTL v1.3 is distributed in the hope that it will be useful,
+# LTTL v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with LTTL v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with LTTL v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 from operator       import itemgetter

_textable/widgets/LTTL/Segmentation.py

 #=============================================================================
 # Class LTTL.Segmentation, v0.20
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the LTTL package v1.3
+# This file is part of the LTTL package v1.4
 #
-# LTTL v1.3 is free software: you can redistribute it and/or modify
+# LTTL v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# LTTL v1.3 is distributed in the hope that it will be useful,
+# LTTL v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with LTTL v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with LTTL v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 import codecs, re

_textable/widgets/LTTL/Segmenter.py

 #=============================================================================
 # Class LTTL.Segmenter, v0.18
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the LTTL package v1.3
+# This file is part of the LTTL package v1.4
 #
-# LTTL v1.3 is free software: you can redistribute it and/or modify
+# LTTL v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# LTTL v1.3 is distributed in the hope that it will be useful,
+# LTTL v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with LTTL v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with LTTL v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 from __future__ import division

_textable/widgets/LTTL/Table.py

 #=============================================================================
-# Module LTTL.Table, v0.06
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Module LTTL.Table, v0.07
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the LTTL package v1.3
+# This file is part of the LTTL package v1.4
 #
-# LTTL v1.3 is free software: you can redistribute it and/or modify
+# LTTL v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# LTTL v1.3 is distributed in the hope that it will be useful,
+# LTTL v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with LTTL v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with LTTL v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 # Provides classes:
 # - Table
                         ]),
                         [0 for v in values]
                 ))
+        elif mode == 'quotients':
+            row_ids   = self.row_ids
+            col_ids   = self.col_ids
+            col_total = list()
+            for col_id in col_ids:
+                col_values = [
+                        self.values.get((row_id, col_id), 0)
+                                for row_id in row_ids
+                ]
+                col_total.append(sum(col_values))
+            total     = sum(col_total)
+            for row_id in row_ids:
+                row_values = [
+                        self.values.get((row_id, col_id), 0)
+                                for col_id in col_ids
+                ]
+                row_total = sum(row_values)
+                for col_idx in xrange(len(col_ids)):
+                    try:
+                        new_value = (
+                                (row_values[col_idx] * total)
+                                /
+                                (row_total * col_total[col_idx])
+                        )
+                    except ZeroDivisionError:
+                        new_value = 0
+                    new_values[(row_id, col_ids[col_idx])] = new_value
         return(Table(
                 list(self.row_ids),
                 list(self.col_ids),

_textable/widgets/LTTL/Utils.py

 #=============================================================================
-# Module LTTL.Utils, v0.03
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Module LTTL.Utils, v0.05
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the LTTL package v1.3
+# This file is part of the LTTL package v1.4
 #
-# LTTL v1.3 is free software: you can redistribute it and/or modify
+# LTTL v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# LTTL v1.3 is distributed in the hope that it will be useful,
+# LTTL v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with LTTL v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with LTTL v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 # Provides functions:
 # - parse_xml_tag()
 from Address      import Address
 
 element_regex   = re.compile(r'(\w+)', re.U)
-attribute_regex = re.compile(r'''(\w+)\s*=\s*['"](.+?)['"]''', re.U)
+attribute_regex = re.compile(r'''(\w+)\s*=\s*(['"])(.+?)(?<!\\)\2''', re.U)
 
 
 def parse_xml_tag(tag):
         tag_description['is_element']       = True
         tag_description['element']          = elem.group(1)
         for attr in re.finditer(attribute_regex, tag):
-            tag_description['attributes'][attr.group(1)] = attr.group(2)
+            tag_description['attributes'][attr.group(1)] = attr.group(3)
         if tag[1] != '/':
             tag_description['is_opening']   = True
         elif tag[-2] == '/':
     
     NB: keys with zero value are removed.
     """
-    return {
-            k[1]: v
-                    for (k, v) in dictionary.iteritems()
-                            if k[0] == key and v > 0
-    }
-
+    return dict(
+        (k[1], v)
+            for (k, v) in dictionary.iteritems()
+                if k[0] == key and v > 0
+    )
 
 def get_average(values, weights=None):
     """Compute the average and standard deviation of a list of values"""

_textable/widgets/OWTextableAnnotation.py

 #=============================================================================
-# Class OWTextableAnnotation, v0.07
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextableAnnotation, v0.09
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
 
     """Orange widget for tabulating annotation information"""
 
+    contextHandlers = {
+        '': SegmentationListContextHandler(
+            '', [
+                ContextInputListField('segmentations'),
+                ContextInputIndex('units'),
+                ContextInputIndex('contexts'),
+                'unitAnnotationKey',
+                'contextAnnotationKey',
+                'sequenceLength',
+                'uuid',
+            ]
+        )
+    }
+
     settingsList = [
             'autoSend',
-            'sequenceLength',
             'intraSeqDelim',
             'sortOrder',
             'sortReverse',
             'keepOnlyFirst',
             'valueDelimiter',
-            'savedUnitSenderUuid',                                                  
-            'savedUnitAnnotationKey',                                               
-            'savedContextSenderUuid',                                               
-            'savedContextAnnotationKey',                                            
+            'sequenceLength',
     ]
 
     def __init__(self, parent=None, signalManager=None):
         self.sortReverse            = True
         self.keepOnlyFirst          = True
         self.valueDelimiter         = u'|'
-        self.savedUnitSenderUuid        = None                                      
-        self.savedUnitAnnotationKey     = None                                      
-        self.savedContextSenderUuid     = None                                      
-        self.savedContextAnnotationKey  = None                                      
+        self.uuid                   = None
         self.loadSettings()
+        self.uuid = getWidgetUuid(self)
 
         # Other attributes...
         self.processor              = Processor()
 
     def inputData(self, newItem, newId=None):
         """Process incoming data."""
+        self.closeContext()
         updateMultipleInputs(
                 self.segmentations,
                 newItem,
                 self.onInputRemoval
         )
         self.infoBox.inputChanged()
-        self.sendButton.sendIf()
+        self.updateGUI()
 
 
     def onInputRemoval(self, index):
         self.adjustSize()
 
 
-
     def handleNewSignals(self):
         """Overridden: called after multiple signals have been added"""
-        try:
-            self.restoreSettings()
-        except AttributeError:
-            pass
-
-    def getSettings(self, alsoContexts = True, globalContexts=False):
-        """Overridden: called when a file is saved (among other situations)"""
-        try:
-            self.storeSettings()
-        except AttributeError:
-            pass
-        return super(type(self), self).getSettings(
-                alsoContexts = True, globalContexts=False
-        )
+        self.openContext("", self.segmentations)
+        self.updateGUI()
+        self.sendButton.sendIf()
 
-    def restoreSettings(self):
-        """When a scheme file is opened, restore those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if not self.settingsRestored:
-            self.settingsRestored = True
-            for segIndex in xrange(len(self.segmentations)):
-                segmentation = self.segmentations[segIndex]
-                if segmentation[0][2].uuid == self.savedUnitSenderUuid:
-                    self.units = segIndex
-                if segmentation[0][2].uuid == self.savedContextSenderUuid:
-                    self.contexts = segIndex
-            self.updateGUI()
-            if self.units is not None:
-                segmentation       = self.segmentations[self.units]
-                unitAnnotationKeys = [u'(none)']
-                unitAnnotationKeys.extend(
-                        segmentation[1].get_annotation_keys()
-                )
-                for key in unitAnnotationKeys:
-                    if key == self.savedUnitAnnotationKey:
-                        self.unitAnnotationKey = key
-                        break
-            if self.contexts is not None:
-                segmentation          = self.segmentations[self.contexts]
-                contextAnnotationKeys = [u'(none)']
-                contextAnnotationKeys.extend(
-                        segmentation[1].get_annotation_keys()
-                )
-                for key in contextAnnotationKeys:
-                    if key == self.savedContextAnnotationKey:
-                        self.contextAnnotationKey = key
-                        break
-            self.sendButton.sendIf()
-
-    def storeSettings(self):
-        """When a scheme file is saved, store those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if self.settingsRestored:
-            if self.units is not None:
-                segmentation                = self.segmentations[self.units]
-                self.savedUnitSenderUuid    = segmentation[0][2].uuid
-                self.savedUnitAnnotationKey = self.unitAnnotationKey
-            else:
-                self.savedUnitSenderUuid    = None
-                self.savedUnitAnnotationKey = None
-            if self.contexts is not None:
-                segmentation               = self.segmentations[self.contexts]
-                self.savedContextSenderUuid    = segmentation[0][2].uuid
-                self.savedContextAnnotationKey = self.contextAnnotationKey
-            else:
-                self.savedContextSenderUuid    = None
-                self.savedContextAnnotationKey = None
 
 
 

_textable/widgets/OWTextableContext.py

 #=============================================================================
-# Class OWTextableContext, v0.05
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextableContext, v0.07
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
 
     """Orange widget for building concordances and studying collocations"""
 
+    contextHandlers = {
+        '': SegmentationListContextHandler(
+            '', [
+                ContextInputListField('segmentations'),
+                ContextInputIndex('units'),
+                ContextInputIndex('contexts'),
+                'unitAnnotationKey',
+                'unitAnnotationKey',
+                'contextAnnotationKey',
+                'separateAnnotation',
+                'maxDistance',
+                'minFrequency',
+                'uuid',
+            ]
+        )
+    }
+    
     settingsList = [
             'autoSend',
             'mode',
             'maxLength',
             'applyMaxDistance',
             'maxDistance',
-            'minFrequency',
             'useCollocationFormat',
-            'savedUnitSenderUuid',
-            'savedUnitAnnotationKey',
-            'savedContextSenderUuid',
-            'savedContextAnnotationKey',
+            'minFrequency',
     ]
 
     def __init__(self, parent=None, signalManager=None):
         self.mode                       = u'Neighboring segments'
         self.separateAnnotation         = False
         self.maxLength                  = 25
-        self.maxDistance                = 10
+        self.maxDistance                = 5
         self.minFrequency               = 1
         self.applyMaxLength             = True
         self.applyMaxDistance           = True
         self.useCollocationFormat       = False
-        self.savedUnitSenderUuid        = None
-        self.savedUnitAnnotationKey     = None
-        self.savedContextSenderUuid     = None
-        self.savedContextAnnotationKey  = None
+        self.uuid                       = None
         self.loadSettings()
+        self.uuid = getWidgetUuid(self)
 
         # Other attributes...
         self.processor              = Processor()
 
     def inputData(self, newItem, newId=None):
         """Process incoming data."""
+        self.closeContext()
         updateMultipleInputs(
                 self.segmentations,
                 newItem,
                 self.onInputRemoval
         )
         self.infoBox.inputChanged()
-        self.sendButton.sendIf()
+        self.updateGUI()
 
 
     def onInputRemoval(self, index):
                 self.containingSegmentationBox.setVisible(False)
                 self.neighboringSegmentsBox.setVisible(True)
                 if (segmentation is not None and len(segmentation)):
-                    self.maxDistance = (self.maxDistance or len(segmentation))
-                    self.maxDistanceSpin[1].setRange(
-                            1,
-                            len(segmentation),
+                    self.maxDistance = (
+                        self.maxDistance or len(segmentation) - 1
                     )
+                    if len(segmentation) == 1:
+                        self.maxDistanceSpin[1].setDisabled(True)
+                    else:
+                        self.maxDistanceSpin[1].setDisabled(False)
+                        self.maxDistanceSpin[1].setRange(
+                                1,
+                                len(segmentation) - 1,
+                        )
                 if self.useCollocationFormat:
                     self.unitsSubBox.setDisabled(True)
                     self.minFrequencySpin.control.setRange(
         self.adjustSize()
 
 
-
     def handleNewSignals(self):
         """Overridden: called after multiple signals have been added"""
-        try:
-            self.restoreSettings()
-        except AttributeError:
-            pass
-
-    def getSettings(self, alsoContexts = True, globalContexts=False):
-        """Overridden: called when a file is saved (among other situations)"""
-        try:
-            self.storeSettings()
-        except AttributeError:
-            pass
-        return super(type(self), self).getSettings(
-                alsoContexts = True, globalContexts=False
-        )
-
-    def restoreSettings(self):
-        """When a scheme file is opened, restore those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if not self.settingsRestored:
-            self.settingsRestored = True
-            for segIndex in xrange(len(self.segmentations)):
-                segmentation = self.segmentations[segIndex]
-                if segmentation[0][2].uuid == self.savedUnitSenderUuid:
-                    self.units = segIndex
-                if segmentation[0][2].uuid == self.savedContextSenderUuid:
-                    self.contexts = segIndex
-            self.updateGUI()
-            if self.units is not None:
-                segmentation       = self.segmentations[self.units]
-                unitAnnotationKeys = [u'(none)']
-                unitAnnotationKeys.extend(segmentation[1].get_annotation_keys())
-                for key in unitAnnotationKeys:
-                    if key == self.savedUnitAnnotationKey:
-                        self.unitAnnotationKey = key
-            if self.contexts is not None:
-                segmentation          = self.segmentations[self.contexts]
-                contextAnnotationKeys = [u'(none)']
-                contextAnnotationKeys.extend(
-                        segmentation[1].get_annotation_keys()
-                )
-                for key in contextAnnotationKeys:
-                    if key == self.savedContextAnnotationKey:
-                        self.contextAnnotationKey = key
-                        break
-            self.sendButton.sendIf()
-
-    def storeSettings(self):
-        """When a scheme file is saved, store those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if self.settingsRestored:
-            if self.units is not None:
-                segmentation                = self.segmentations[self.units]
-                self.savedUnitSenderUuid    = segmentation[0][2].uuid
-                self.savedUnitAnnotationKey = self.unitAnnotationKey
-            else:
-                self.savedUnitSenderUuid    = None
-                self.savedUnitAnnotationKey = None
-            if self.contexts is not None:
-                segmentation = self.segmentations[self.contexts]
-                self.savedContextSenderUuid    = segmentation[0][2].uuid
-                self.savedContextAnnotationKey = self.contextAnnotationKey
-            else:
-                self.savedContextSenderUuid    = None
-                self.savedContextAnnotationKey = None
+        self.openContext("", self.segmentations)
+        self.updateGUI()
+        self.sendButton.sendIf()
 
 
 

_textable/widgets/OWTextableConvert.py

 #=============================================================================
-# Class OWTextableConvert, v0.07
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextableConvert, v0.09
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
                 widget              = self.transformBoxLine4,
                 master              = self,
                 value               = 'normalizeMode',
-                items               = [u'rows', u'columns', u'table'],
+                items               = [
+                                            u'rows',
+                                            u'columns',
+                                            u'table',
+                                            u'quotients'
+                                    ],
                 sendSelectedValue   = True,
                 callback            = self.sendButton.settingsChanged,
                 tooltip             = (
                         u"Select the units to which normalization will be\n"
-                        u"applied: rows, columns, or the entire table."
+                        u"applied: rows, columns, or the entire table;\n"
+                        u"in 'quotients' mode, the count stored in each\n"
+                        u"cell is divided by the corresponding theoretical\n"
+                        u"count under independence: the result is greater\n"
+                        u"than 1 in case of attraction between line and\n"
+                        u"column, lesser than 1 in case of repulsion, and\n"
+                        u"1 if there is no specific interaction between them."
                 ),
         )
         self.normalizeModeCombo.setMinimumWidth(150)
             self.exportButton.setDisabled(False)
 
             if self.displayAdvancedSettings:
+                self.normalizeTypeCombo.setDisabled(True)
                 if self.sortRows:
                     self.sortRowsKeyIdCombo.clear()
                     self.sortRowsKeyIdCombo.addItem(
                         self.transformBoxLine4.setDisabled(False)
                         if self.normalize:
                             self.normalizeModeCombo.setDisabled(False)
-                            self.normalizeTypeCombo.setDisabled(False)
+                            if self.normalizeMode != u'quotients':
+                                self.normalizeTypeCombo.setDisabled(False)
                             self.transformBoxLine5.setDisabled(True)
                         else:
                             self.normalizeModeCombo.setDisabled(True)

_textable/widgets/OWTextableCount.py

 #=============================================================================
-# Class OWTextableCount, v0.15
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextableCount, v0.18
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
 
     """Orange widget for counting text units"""
 
+    contextHandlers = {
+        '': SegmentationListContextHandler(
+            '', [
+                ContextInputListField('segmentations'),
+                ContextInputIndex('units'),
+                ContextInputIndex('contexts'),
+                'mode',
+                'unitAnnotationKey',
+                'contextAnnotationKey',
+                'sequenceLength',
+                'windowSize',
+                'leftContextSize',
+                'rightContextSize',
+                'uuid',
+            ]
+        )
+    }
+
     settingsList = [
             'autoSend',
-            'sequenceLength',
             'intraSeqDelim',
-            'mode',
+            'unitPosMarker',
             'mergeContexts',
+            'sequenceLength',
             'windowSize',
             'leftContextSize',
             'rightContextSize',
-            'unitPosMarker',
-            'savedUnitSenderUuid',                                                  
-            'savedUnitAnnotationKey',                                               
-            'savedContextSenderUuid',                                               
-            'savedContextAnnotationKey',                                            
-            'savedMode',                                                            
     ]
 
     def __init__(self, parent=None, signalManager=None):
         self.leftContextSize            = 0
         self.rightContextSize           = 0
         self.unitPosMarker              = u'_'
-        self.savedUnitSenderUuid        = None                                      
-        self.savedUnitAnnotationKey     = None                                      
-        self.savedContextSenderUuid     = None                                      
-        self.savedContextAnnotationKey  = None                                      
-        self.savedMode                  = None                                      
+        self.uuid                       = None
         self.loadSettings()
+        self.uuid = getWidgetUuid(self)
+
+
+
 
         # Other attributes...
         self.processor              = Processor()
         self.unitAnnotationKey      = None
         self.contexts               = None
         self.contextAnnotationKey   = None
-        self.settingsRestored       = False                                         
         self.infoBox                = InfoBox(
                 widget          = self.controlArea,
                 stringClickSend = u"Please click 'Compute' when ready.",
                 callback            = self.sendButton.settingsChanged,
                 tooltip             = (
                         u"The segmentation whose segments will be counted.\n"
-                        u"This defines the rows of the resulting crosstab."
+                        u"This defines the columns of the resulting crosstab."
                 ),
         )
         self.unitSegmentationCombo.setMinimumWidth(120)
 
     def inputData(self, newItem, newId=None):
         """Process incoming data."""
+        self.closeContext()
         updateMultipleInputs(
                 self.segmentations,
                 newItem,
                 self.onInputRemoval
         )
         self.infoBox.inputChanged()
-        self.sendButton.sendIf()
+        self.updateGUI()
 
 
     def onInputRemoval(self, index):
 
     def handleNewSignals(self):
         """Overridden: called after multiple signals have been added"""
-        try:
-            self.restoreSettings()
-        except AttributeError:
-            pass
-
-    def getSettings(self, alsoContexts = True, globalContexts=False):
-        """Overridden: called when a file is saved (among other situations)"""
-        try:
-            self.storeSettings()
-        except AttributeError:
-            pass
-        return super(type(self), self).getSettings(
-                alsoContexts = True, globalContexts=False
-        )
-
-    def restoreSettings(self):
-        """When a scheme file is opened, restore those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if not self.settingsRestored:
-            self.settingsRestored = True
-            self.mode             = self.savedMode
-            for segIndex in xrange(len(self.segmentations)):
-                segmentation = self.segmentations[segIndex]
-                if segmentation[0][2].uuid == self.savedUnitSenderUuid:
-                    self.units = segIndex
-                if self.mode == u'Containing segmentation':
-                    if segmentation[0][2].uuid == self.savedContextSenderUuid:
-                        self.contexts = segIndex
-            self.updateGUI()
-            if self.units is not None:
-                segmentation       = self.segmentations[self.units]
-                unitAnnotationKeys = [u'(none)']
-                unitAnnotationKeys.extend(
-                        segmentation[1].get_annotation_keys()
-                )
-                for key in unitAnnotationKeys:
-                    if key == self.savedUnitAnnotationKey:
-                        self.unitAnnotationKey = key
-                        break
-                if self.mode == u'Containing segmentation':
-                    if self.contexts is not None:
-                        segmentation = self.segmentations[self.contexts]
-                        contextAnnotationKeys = [u'(none)']
-                        contextAnnotationKeys.extend(
-                                segmentation[1].get_annotation_keys()
-                        )
-                        for key in contextAnnotationKeys:
-                            if key == self.savedContextAnnotationKey:
-                                self.contextAnnotationKey = key
-                                break
-            self.sendButton.sendIf()
-
-    def storeSettings(self):
-        """When a scheme file is saved, store those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if self.settingsRestored:
-            self.savedMode                  = self.mode
-            if self.units is not None:
-                segmentation                = self.segmentations[self.units]
-                self.savedUnitSenderUuid    = segmentation[0][2].uuid
-                self.savedUnitAnnotationKey = self.unitAnnotationKey
-                if          self.mode == u'Containing segmentation' \
-                        and self.contexts is not None:
-                    segmentation = self.segmentations[self.contexts]
-                    self.savedContextSenderUuid = segmentation[0][2].uuid
-                    self.savedContextAnnotationKey \
-                            = self.contextAnnotationKey
-                else:
-                    self.savedContextSenderUuid    = None
-                    self.savedContextAnnotationKey = None
-            else:
-                self.savedUnitSenderUuid    = None
-                self.savedUnitAnnotationKey = None
+        self.openContext("", self.segmentations)
+        self.updateGUI()
+        self.sendButton.sendIf()
 
 
 

_textable/widgets/OWTextableDisplay.py

 #=============================================================================
-# Class OWTextableDisplay, v0.09
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextableDisplay, v0.11
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
     """A widget for displaying segmentations"""
 
     settingsList = [
+            'displayAdvancedSettings',
             'autoSend',
             'customFormatting',
             'customFormat',
         ]
         
         # Settings...
-        self.customFormatting       = False
-        self.customFormat           = u''
-        self.segmentDelimiter       = u''
-        self.encoding               = 'utf-8'
-        self.lastLocation           = '.'
-        self.autoSend               = True
-        self.uuid                   = uuid.uuid4()
+        self.displayAdvancedSettings    = False
+        self.customFormatting           = False
+        self.customFormat               = u''
+        self.segmentDelimiter           = u''
+        self.encoding                   = 'utf-8'
+        self.lastLocation               = '.'
+        self.autoSend                   = True
+        self.uuid                       = None
         self.loadSettings()
+        self.uuid                       = getWidgetUuid(self)
 
         # Other attributes...
         self.segmentation           = None
 
         # GUI...
 
+        # NB: These are "custom" advanced settings, not those provided by
+        # TextableUtils. Note also that there are two copies of the checkbox
+        # controlling the same attribute, to simulate its moving from one
+        # side of the widget to the other...
+        self.advancedSettingsCheckBoxLeft = OWGUI.checkBox(
+                widget              = self.controlArea,
+                master              = self,
+                value               = 'displayAdvancedSettings',
+                label               = u'Advanced settings',
+                callback            = self.sendButton.settingsChanged,
+                tooltip             = (
+                        u"Toggle advanced settings on and off."
+                ),
+        )
+        OWGUI.separator(
+                widget              = self.controlArea,
+                height              = 3,
+        )
+
         # Custom formatting box...
         formattingBox = OWGUI.widgetBox(
                 widget              = self.controlArea,
         self.sendButton.draw()
 
         # Main area
+        
+        # NB: This is the second copy of the advanced settings checkbox,
+        # see above...
+        self.advancedSettingsRightBox = OWGUI.widgetBox(
+                widget              = self.mainArea,
+                orientation         = 'vertical',
+        )
+        self.advancedSettingsCheckBoxRight = OWGUI.checkBox(
+                widget              = self.advancedSettingsRightBox,
+                master              = self,
+                value               = 'displayAdvancedSettings',
+                label               = u'Advanced settings',
+                callback            = self.sendButton.settingsChanged,
+                tooltip             = (
+                        u"Toggle advanced settings on and off."
+                ),
+        )
+        OWGUI.separator(
+                widget              = self.advancedSettingsRightBox,
+                height              = 3,
+        )
+
+        self.advancedSettingsCheckBoxRightPlaceholder = OWGUI.separator(
+                widget              = self.mainArea,
+                height              = 25,
+        )
+
         self.navigationBox = OWGUI.widgetBox(
                 widget              = self.mainArea,
                 orientation         = 'vertical',
 
     def updateGUI(self):
         """Update GUI state"""
+        self.controlArea.setVisible(self.displayAdvancedSettings)
+        self.advancedSettingsCheckBoxRightPlaceholder.setVisible(
+                self.displayAdvancedSettings
+        )
+        self.advancedSettingsCheckBoxLeft.setVisible(
+                self.displayAdvancedSettings
+        )
+        self.advancedSettingsRightBox.setVisible(
+                not self.displayAdvancedSettings
+        )
         self.browser.clear()
         if self.segmentation:
-            if self.customFormatting:
+            if self.displayAdvancedSettings:
+                customFormatting = self.customFormatting
+            else:
+                customFormatting = False
+                self.autoSend    = True
+            if customFormatting:
                 self.formattingIndentedBox.setDisabled(False)
                 if self.customFormat:
                     progressBar = OWGUI.ProgressBar(
                         self.customFormat.decode('string_escape'),
                         self.segmentDelimiter.decode('string_escape'),
                         True,
-                        progress_callback       = progressBar.advance,
+                        progress_callback = progressBar.advance,
                     )
                     progressBar.finish()
                 else:
                     displayedString,
                     label           = u'displayed_segmentation',
             )
-            if self.customFormatting == False:
+            if customFormatting == False:
                 self.navigationBox.setDisabled(False)
                 self.gotoSpin.control.setRange(1, len(self.segmentation))
                 if self.goto:

_textable/widgets/OWTextableExtractXML.py

 #=============================================================================
-# Class OWTextableExtractXML, v0.08
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextableExtractXML, v0.09
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
 <priority>4005</priority>
 """
 
-import re, uuid
+import re
 
 from LTTL.Segmenter    import Segmenter
 from LTTL.Segmentation import Segmentation
         self.importElementAs            = u''
         self.mergeDuplicates            = False
         self.preserveLeaves             = False
-        self.deleteMarkup               = True
+        self.deleteMarkup               = False
         self.displayAdvancedSettings    = False
-        self.uuid                       = uuid.uuid4()
+        self.uuid                       = None
         self.loadSettings()
+        self.uuid                       = getWidgetUuid(self)
 
         # Other attributes...
         self.segmenter                  = Segmenter()

_textable/widgets/OWTextableIntersect.py

 #=============================================================================
-# Class OWTextableIntersect, v0.10
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextableIntersect, v0.11
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
 <priority>4004</priority>
 """
 
-import uuid
-
 from LTTL.Segmenter    import Segmenter
 from LTTL.Segmentation import Segmentation
 
 
     """Orange widget for segment in-/exclusion based on other segmentation"""
     
+    contextHandlers = {
+        '': SegmentationListContextHandler(
+            '', [
+                ContextInputListField('segmentations'),
+                ContextInputIndex('source'),
+                ContextInputIndex('filtering'),
+                'sourceAnnotationKey',
+                'filteringAnnotationKey',
+            ]
+        )
+    }
+
     settingsList = [
             'mode',
             'copyAnnotations',
             'autoNumberKey',
             'displayAdvancedSettings',
             'uuid',
-            'savedSourceSenderUuid',                                                  
-            'savedSourceAnnotationKey',                                               
-            'savedFilteringSenderUuid',                                                  
-            'savedFilteringAnnotationKey',                                               
     ]
 
     def __init__(self, parent=None, signalManager=None):
         self.autoNumber                     = False
         self.autoNumberKey                  = u'num'
         self.displayAdvancedSettings        = False
-        self.uuid                           = uuid.uuid4()
-        self.savedSourceSenderUuid          = None                                      
-        self.savedSourceAnnotationKey       = None                                      
-        self.savedFilteringSenderUuid       = None                                      
-        self.savedFilteringAnnotationKey    = None                                      
+        self.uuid                           = None
         self.loadSettings()
+        self.uuid                           = getWidgetUuid(self)
 
         # Other attributes...
         self.segmenter              = Segmenter()
 
         # Source and filtering parameter...
         source = {'segmentation': self.segmentations[self.source][1]}
+        source['annotation_key'] = self.sourceAnnotationKey or None
         if self.sourceAnnotationKey == u'(none)':
             source['annotation_key'] = None
         filtering = {
 
     def inputData(self, newItem, newId=None):
         """Process incoming data."""
+        self.closeContext()
         updateMultipleInputs(
                 self.segmentations,
                 newItem,
                 self.onInputRemoval
         )
         self.infoBox.inputChanged()
-        self.sendButton.sendIf()
+        self.updateGUI()
 
 
     def onInputRemoval(self, index):
 
     def handleNewSignals(self):
         """Overridden: called after multiple signals have been added"""
-        try:
-            self.restoreSettings()
-        except AttributeError:
-            pass
-
-    def getSettings(self, alsoContexts = True, globalContexts=False):
-        """Overridden: called when a file is saved (among other situations)"""
-        try:
-            self.storeSettings()
-        except AttributeError:
-            pass
-        return super(type(self), self).getSettings(
-                alsoContexts = True, globalContexts=False
-        )
-
-    def restoreSettings(self):
-        """When a scheme file is opened, restore those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if not self.settingsRestored:
-            self.settingsRestored = True
-            for segIndex in xrange(len(self.segmentations)):
-                segmentation = self.segmentations[segIndex]
-                if segmentation[0][2].uuid == self.savedSourceSenderUuid:
-                    self.source = segIndex
-                if segmentation[0][2].uuid == self.savedFilteringSenderUuid:
-                    self.filtering = segIndex
-            self.updateGUI()
-            if self.source is not None:
-                segmentation         = self.segmentations[self.source]
-                sourceAnnotationKeys = [u'(none)']
-                sourceAnnotationKeys.extend(
-                        segmentation[1].get_annotation_keys()
-                )
-                for key in sourceAnnotationKeys:
-                    if key == self.savedSourceAnnotationKey:
-                        self.sourceAnnotationKey = key
-                        break
-                if self.displayAdvancedSettings:
-                    if self.filtering is not None:
-                        segmentation = self.segmentations[self.filtering]
-                        filteringAnnotationKeys = [u'(none)']
-                        filteringAnnotationKeys.extend(
-                                segmentation[1].get_annotation_keys()
-                        )
-                        for key in filteringAnnotationKeys:
-                            if key == self.savedFilteringAnnotationKey:
-                                self.filteringAnnotationKey = key
-                                break
-            self.sendButton.sendIf()
-
-    def storeSettings(self):
-        """When a scheme file is saved, store those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if self.settingsRestored:
-            if self.source is not None:
-                segmentation                 = self.segmentations[self.source]
-                self.savedSourceSenderUuid   = segmentation[0][2].uuid
-                self.savedSourceAnnotationKey = self.sourceAnnotationKey
-                segmentation = self.segmentations[self.filtering]
-            else:
-                self.savedSourceSenderUuid    = None
-                self.savedSourceAnnotationKey = None
-            if self.filtering is not None:
-                self.savedFilteringSenderUuid = segmentation[0][2].uuid
-                if self.displayAdvancedSettings:
-                    self.savedFilteringAnnotationKey \
-                            = self.filteringAnnotationKey
-            else:
-                self.savedFilteringSenderUuid    = None
-                self.savedFilteringAnnotationKey = None
+        self.openContext("", self.segmentations)
+        self.updateGUI()
+        self.sendButton.sendIf()
 
 
 

_textable/widgets/OWTextableLength.py

 #=============================================================================
-# Class OWTextableLength, v0.10
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextableLength, v0.11
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
 
     """Orange widget for length computation"""
 
+    contextHandlers = {
+        '': SegmentationListContextHandler(
+            '', [
+                ContextInputListField('segmentations'),
+                ContextInputIndex('units'),
+                ContextInputIndex('averagingSegmentation'),
+                ContextInputIndex('contexts'),
+                'mode',
+                'unitAnnotationKey',
+                'contextAnnotationKey',
+                'sequenceLength',
+            ]
+        )
+    }
+
     settingsList = [
             'autoSend',
-            'computeAverage',
             'computeStdev',
-            'mode',
             'mergeContexts',
-            'windowSize',
-            'savedUnitSenderUuid',                                                  
-            'savedAveragingSenderUuid',                                             
-            'savedContextSenderUuid',                                               
-            'savedContextAnnotationKey',                                            
-            'savedMode',                                                            
+            'computeAverage',
+            'sequenceLength',
     ]
 
     def __init__(self, parent=None, signalManager=None):
         self.mode                       = u'No context'
         self.mergeContexts              = False
         self.windowSize                 = 1
-        self.savedUnitSenderUuid        = None                                      
-        self.savedAveragingSenderUuid   = None                                      
-        self.savedContextSenderUuid     = None                                      
-        self.savedContextAnnotationKey  = None                                      
-        self.savedMode                  = None                                      
         self.loadSettings()
 
         # Other attributes...
 
     def inputData(self, newItem, newId=None):
         """Process incoming data."""
+        self.closeContext()
         updateMultipleInputs(
                 self.segmentations,
                 newItem,
                 self.onInputRemoval
         )
         self.infoBox.inputChanged()
-        self.sendButton.sendIf()
+        self.updateGUI()
 
 
     def sendData(self):
 
     def handleNewSignals(self):
         """Overridden: called after multiple signals have been added"""
-        try:
-            self.restoreSettings()
-        except AttributeError:
-            pass
-
-    def getSettings(self, alsoContexts = True, globalContexts=False):
-        """Overridden: called when a file is saved (among other situations)"""
-        try:
-            self.storeSettings()
-        except AttributeError:
-            pass
-        return super(type(self), self).getSettings(
-                alsoContexts = True, globalContexts=False
-        )
-
-    def restoreSettings(self):
-        """When a scheme file is opened, restore those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if not self.settingsRestored:
-            self.settingsRestored = True
-            self.mode             = self.savedMode
-            for segIndex in xrange(len(self.segmentations)):
-                segmentation = self.segmentations[segIndex]
-                if segmentation[0][2].uuid == self.savedUnitSenderUuid:
-                    self.units = segIndex
-                if segmentation[0][2].uuid == self.savedAveragingSenderUuid:
-                    self.averagingSegmentation = segIndex
-                if self.mode == u'Containing segmentation':
-                    if segmentation[0][2].uuid == self.savedContextSenderUuid:
-                        self.contexts = segIndex
-            self.updateGUI()
-            if self.mode == u'Containing segmentation':
-                segmentation          = self.segmentations[self.contexts]
-                contextAnnotationKeys = [u'(none)']
-                contextAnnotationKeys.extend(
-                        segmentation[1].get_annotation_keys()
-                )
-                for key in contextAnnotationKeys:
-                    if key == self.savedContextAnnotationKey:
-                        self.contextAnnotationKey = key
-                        break
-            self.sendButton.sendIf()
-
-    def storeSettings(self):
-        """When a scheme file is saved, store those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if self.settingsRestored:
-            self.savedMode                  = self.mode
-            segmentation                    = self.segmentations[self.units]
-            self.savedUnitSenderUuid        = segmentation[0][2].uuid
-            segmentation = self.segmentations[self.averagingSegmentation]
-            self.savedAveragingSenderUuid   = segmentation[0][2].uuid
-            if self.mode == u'Containing segmentation':
-                segmentation = self.segmentations[self.contexts]
-                self.savedContextSenderUuid    = segmentation[0][2].uuid
-                self.savedContextAnnotationKey = self.contextAnnotationKey
-        if self.settingsRestored:
-            self.savedMode                  = self.mode
-            if self.units is not None:
-                segmentation                = self.segmentations[self.units]
-                self.savedUnitSenderUuid    = segmentation[0][2].uuid
-                if self.units is not None:
-                    segmentation = \
-                            self.segmentations[self.averagingSegmentation]
-                    self.savedAveragingSenderUuid   = segmentation[0][2].uuid
-                else:
-                    self.savedAveragingSenderUuid   = None
-                if          self.mode == u'Containing segmentation' \
-                        and self.contexts is not None:
-                    segmentation = self.segmentations[self.contexts]
-                    self.savedContextSenderUuid = segmentation[0][2].uuid
-                    self.savedContextAnnotationKey \
-                            = self.contextAnnotationKey
-                else:
-                    self.savedContextSenderUuid    = None
-                    self.savedContextAnnotationKey = None
-            else:
-                self.savedUnitSenderUuid    = None
-                self.savedUnitAnnotationKey = None
+        self.openContext("", self.segmentations)
+        self.updateGUI()
+        self.sendButton.sendIf()
 
 
 

_textable/widgets/OWTextableMerge.py

 #=============================================================================
-# Class OWTextableMerge, v0.15
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextableMerge, v0.16
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
 <priority>4001</priority>
 """
 
-
-import uuid
-
 from LTTL.Segmentation import Segmentation
 from LTTL.Input        import Input
 from LTTL.Segmenter    import Segmenter
 
     """Orange widget for merging segmentations"""
     
+    contextHandlers = {
+        "": SegmentationListContextHandler(
+            "", [
+                ContextInputListField("texts"),
+                ContextListField("textLabels", selected="selectedTextLabels")
+            ],
+        )
+    }
+
     settingsList = [
             'autoSend',
             'copyAnnotations',
             'mergeDuplicates',
             'displayAdvancedSettings',
             'uuid',
-            'savedSenderUuidOrder',
     ]
     
     def __init__(self, parent=None, signalManager=None):
         self.sortSegments               = False
         self.mergeDuplicates            = False
         self.displayAdvancedSettings    = False
-        self.uuid                       = uuid.uuid4()
         self.savedSenderUuidOrder       = []
+        self.uuid                       = None
         self.loadSettings()
+        self.uuid                       = getWidgetUuid(self)
 
         # Other attributes...
         self.segmenter              = Segmenter();
         self.texts                  = [];
         self.textLabels             = [];
         self.selectedTextLabels     = [];
-        self.settingsRestored       = False                                         
         self.infoBox                = InfoBox(widget=self.controlArea)
         self.sendButton             = SendButton(
                 widget              = self.controlArea,
                 master              = self,
                 value               = 'selectedTextLabels',
                 labels              = 'textLabels',
-                callback            = self.sendButton.settingsChanged,
+                callback            = self.updateGUI,
                 tooltip             = (
                         u"List of segmentations that will be merged\n\n"
                         u"By default, segments of the input segmentations\n"
 
     def inputData(self, newItem, newId=None):
         """Process incoming data."""
+        self.closeContext()
         updateMultipleInputs(self.texts, newItem, newId)
         self.textLabels = [text[1].label for text in self.texts]
         self.infoBox.inputChanged()
-        self.sendButton.sendIf()
 
 
     def updateGUI(self):
                 self.sendButton.settingsChanged()
 
 
-
     def handleNewSignals(self):
         """Overridden: called after multiple signals have been added"""
-        try:
-            self.restoreSettings()
-        except AttributeError:
-            pass
-
-    def getSettings(self, alsoContexts = True, globalContexts=False):
-        """Overridden: called when a file is saved (among other situations)"""
-        try:
-            self.storeSettings()
-        except AttributeError:
-            pass
-        return super(type(self), self).getSettings(
-                alsoContexts = True, globalContexts=False
-        )
-
-    def restoreSettings(self):
-        """When a sheme file is opened, restore those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if not self.settingsRestored:
-            self.settingsRestored = True
-            oldTexts = self.texts[:]
-            for senderUuidIndex in xrange(len(self.savedSenderUuidOrder)):
-                for text in oldTexts:
-                    senderUuid = self.savedSenderUuidOrder[senderUuidIndex]
-                    if text[0][2].uuid == senderUuid:
-                        self.texts[senderUuidIndex] = text
-                        break
-            self.textLabels = [text[1].label for text in self.texts]
-            self.sendButton.sendIf()
-
-    def storeSettings(self):
-        """When a scheme file is saved, store those settings that depend
-        on the particular segmentations that enter this widget.
-        """
-        if self.settingsRestored:
-            self.savedSenderUuidOrder = [t[0][2].uuid for t in self.texts]
+        self.openContext("", self.texts)
+        self.updateGUI()
+        self.sendButton.sendIf()
+
+
+
 
 
 

_textable/widgets/OWTextablePreprocess.py

 #=============================================================================
-# Class OWTextablePreprocess, v0.07
-# Copyright 2012-2013 LangTech Sarl (info@langtech.ch)
+# Class OWTextablePreprocess, v0.08
+# Copyright 2012-2014 LangTech Sarl (info@langtech.ch)
 #=============================================================================
-# This file is part of the Textable (v1.3) extension to Orange Canvas.
+# This file is part of the Textable (v1.4) extension to Orange Canvas.
 #
-# Textable v1.3 is free software: you can redistribute it and/or modify
+# Textable v1.4 is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
-# Textable v1.3 is distributed in the hope that it will be useful,
+# Textable v1.4 is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Textable v1.3. If not, see <http://www.gnu.org/licenses/>.
+# along with Textable v1.4. If not, see <http://www.gnu.org/licenses/>.
 #=============================================================================
 
 """
 <name>Preprocess</name>
-<description>Standard text preprocessing</description>
+<description>Basic text preprocessing</description>
 <icon>icons/Preprocess.png</icon>
 <priority>2001</priority>
 """