Ignore:
Files:
4 added
11 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/application/canvasmain.py

    r11375 r11392  
    2020    Qt, QEvent, QSize, QUrl, QTimer, QFile, QByteArray 
    2121) 
     22 
     23from PyQt4.QtNetwork import QNetworkDiskCache 
    2224 
    2325from PyQt4.QtWebKit import QWebView 
     
    352354        self.help_dock.setAllowedAreas(Qt.NoDockWidgetArea) 
    353355        self.help_view = QWebView() 
     356        manager = self.help_view.page().networkAccessManager() 
     357        cache = QNetworkDiskCache() 
     358        cache.setCacheDirectory( 
     359            os.path.join(config.cache_dir(), "help", "help-view-cache") 
     360        ) 
     361        manager.setCache(cache) 
    354362        self.help_dock.setWidget(self.help_view) 
    355363        self.help_dock.hide() 
     
    856864 
    857865        new_scheme = self.new_scheme_from(filename) 
    858  
    859         self.set_new_scheme(new_scheme) 
    860  
    861         scheme_doc_widget = self.current_document() 
    862         scheme_doc_widget.setPath(filename) 
    863  
    864         self.add_recent_scheme(new_scheme.title, filename) 
     866        if new_scheme is not None: 
     867            self.set_new_scheme(new_scheme) 
     868 
     869            scheme_doc_widget = self.current_document() 
     870            scheme_doc_widget.setPath(filename) 
     871 
     872            self.add_recent_scheme(new_scheme.title, filename) 
    865873 
    866874    def new_scheme_from(self, filename): 
    867875        """Create and return a new :class:`widgetsscheme.WidgetsScheme` 
    868         from a saved `filename`. 
     876        from a saved `filename`. Return `None` if an error occurs. 
    869877 
    870878        """ 
     
    873881        try: 
    874882            parse_scheme(new_scheme, open(filename, "rb"), 
    875                          error_handler=errors.append) 
     883                         error_handler=errors.append, 
     884                         allow_pickle_data=True) 
    876885        except Exception: 
    877886            message_critical( 
    878                  self.tr("Could not load Orange Scheme file"), 
     887                 self.tr("Could not load an Orange Scheme file"), 
    879888                 title=self.tr("Error"), 
    880889                 informative_text=self.tr("An unexpected error occurred " 
    881                                           "while loading %r.") % filename, 
     890                                          "while loading '%s'.") % filename, 
    882891                 exc_info=True, 
    883892                 parent=self) 
     
    885894        if errors: 
    886895            message_warning( 
    887                 self.tr("Errors occured while loading the scheme."), 
     896                self.tr("Errors occurred while loading the scheme."), 
    888897                title=self.tr("Problem"), 
    889898                informative_text=self.tr( 
     
    914923 
    915924    def set_new_scheme(self, new_scheme): 
    916         """Set new_scheme as the current shown scheme. 
     925        """ 
     926        Set new_scheme as the current shown scheme. The old scheme 
     927        will be deleted. 
     928 
    917929        """ 
    918930        scheme_doc = self.current_document() 
     
    926938 
    927939        old_scheme.save_widget_settings() 
     940        old_scheme.close_all_open_widgets() 
     941 
    928942        old_scheme.deleteLater() 
    929943 
     
    982996 
    983997        if document.path() and self.check_can_save(document, document.path()): 
    984             curr_scheme.save_to(open(document.path(), "wb")) 
     998            curr_scheme.save_to(open(document.path(), "wb"), 
     999                                pretty=True, pickle_fallback=True) 
     1000 
    9851001            document.setModified(False) 
    9861002            self.add_recent_scheme(curr_scheme.title, document.path()) 
     
    10251041 
    10261042            try: 
    1027                 curr_scheme.save_to(open(filename, "wb")) 
     1043                curr_scheme.save_to(open(filename, "wb"), 
     1044                                    pretty=True, pickle_fallback=True) 
    10281045            except Exception: 
    10291046                log.error("Error saving %r to %r", curr_scheme, filename, 
     
    11411158 
    11421159            new_scheme = self.new_scheme_from(unicode(selected.path())) 
    1143  
    1144             self.set_new_scheme(new_scheme) 
     1160            if new_scheme is not None: 
     1161                self.set_new_scheme(new_scheme) 
    11451162 
    11461163        return status 
     
    14471464        scheme = document.scheme() 
    14481465        scheme.save_widget_settings() 
     1466        scheme.close_all_open_widgets() 
    14491467 
    14501468        # Set an empty scheme to clear the document 
  • Orange/OrangeCanvas/application/tutorials/310-clustering.ows

    r11324 r11382  
    88        <node id="4" name="Distance Map" position="(454.0, 208.0)" project_name="Orange" qualified_name="Orange.OrangeWidgets.Unsupervised.OWDistanceMap.OWDistanceMap" title="Distance Map" version="" /> 
    99        <node id="5" name="Data Table" position="(640.0, 308.0)" project_name="Orange" qualified_name="Orange.OrangeWidgets.Data.OWDataTable.OWDataTable" title="Data Table (1)" version="" /> 
    10         <node id="6" name="Edit Domain" position="(790.0, 308.0)" project_name="Orange" qualified_name="Orange.OrangeWidgets.Data.OWEditDomain.OWEditDomain" title="Edit Domain" version="" /> 
    1110    </nodes> 
    1211    <links> 
     
    107106s.</properties> 
    108107        <properties format="pickle" node_id="3">(dp1 
    109 S'AutoResize' 
     108S'HDSize' 
    110109p2 
    111 I0 
     110I500 
    112111sS'TextSize' 
    113112p3 
    114113I8 
    115 sS'PrintDepthCheck' 
     114sS'LineSpacing' 
    116115p4 
    117 I0 
    118 sS'LineSpacing' 
    119 p5 
    120116I4 
    121117sS'addIdAs' 
     118p5 
     119I0 
     120sS'PrintDepth' 
    122121p6 
    123 I0 
    124 sS'PrintDepth' 
    125 p7 
    126122I10 
    127123sS'AppendClusters' 
     124p7 
     125I0 
     126sS'CommitOnChange' 
    128127p8 
    129 I0 
    130 sS'VDSize' 
     128I01 
     129sS'AutoResize' 
    131130p9 
    132 I800 
     131I0 
    133132sS'widgetShown' 
    134133p10 
     
    138137S'\x01\xd9\xd0\xcb\x00\x01\x00\x00\x00\x00\x00\xce\x00\x00\x00%\x00\x00\x04\xce\x00\x00\x03^\x00\x00\x00\xce\x00\x00\x00;\x00\x00\x04\xce\x00\x00\x03^\x00\x00\x00\x00\x00\x00' 
    139138p12 
    140 sS'Linkage' 
     139sS'ClassifyName' 
    141140p13 
    142 I3 
    143 sS'HDSize' 
     141S'HC_class' 
    144142p14 
    145 I500 
     143sS'PrintDepthCheck' 
     144p15 
     145I0 
    146146sS'SelectionMode' 
    147 p15 
    148 I01 
    149 sS'ClassifyName' 
    150147p16 
    151 S'HC_class' 
     148I00 
     149sS'VDSize' 
    152150p17 
     151I800 
    153152sS'Annotation' 
    154153p18 
    155154I6 
    156 sS'CommitOnChange' 
     155sS'Linkage' 
    157156p19 
    158 I0 
     157I3 
    159158sS'ManualHorSize' 
    160159p20 
     
    171170p4 
    172171I1 
    173 sS'SquareCells' 
     172sS'ShowBalloon' 
    174173p5 
    175 I01 
    176 sS'CutLow' 
     174I1 
     175sS'CutEnabled' 
    177176p6 
    178177I0 
    179 sS'CutEnabled' 
     178sS'ShowLabels' 
    180179p7 
    181 I0 
    182 sS'ShowLabels' 
     180I1 
     181sS'savedGrid' 
    183182p8 
    184183I1 
    185 sS'SendOnRelease' 
     184sS'palette' 
    186185p9 
    187 I1 
    188 sS'savedGrid' 
    189 p10 
    190 I1 
    191 sS'palette' 
    192 p11 
    193 (lp12 
     186(lp10 
    194187L4278190335L 
    195188aL4278256125L 
     
    449442aL4288716964L 
    450443asS'ShowItemsInBalloon' 
     444p11 
     445I1 
     446sS'CutLow' 
     447p12 
     448I0 
     449sS'Merge' 
    451450p13 
    452451I1 
     452sS'savedWidgetGeometry' 
     453p14 
     454S'\x01\xd9\xd0\xcb\x00\x01\x00\x00\x00\x00\x01\xe0\x00\x00\x01h\x00\x00\x06Z\x00\x00\x04[\x00\x00\x01\xe0\x00\x00\x01~\x00\x00\x06Z\x00\x00\x04[\x00\x00\x00\x00\x00\x00' 
     455p15 
     456sS'colorSettings' 
     457p16 
     458NsS'Grid' 
     459p17 
     460I0 
    453461sS'widgetShown' 
    454 p14 
    455 I0 
    456 sS'Merge' 
    457 p15 
    458 I1 
    459 sS'savedWidgetGeometry' 
    460 p16 
    461 S'\x01\xd9\xd0\xcb\x00\x01\x00\x00\x00\x00\x01\xe0\x00\x00\x01h\x00\x00\x06Z\x00\x00\x04[\x00\x00\x01\xe0\x00\x00\x01~\x00\x00\x06Z\x00\x00\x04[\x00\x00\x00\x00\x00\x00' 
    462 p17 
    463 sS'colorSettings' 
    464462p18 
    465 NsS'Grid' 
     463I0 
     464sS'SendOnRelease' 
    466465p19 
    467 I0 
    468 sS'CutHigh' 
     466I1 
     467sS'SquareCells' 
    469468p20 
    470 I0 
     469I01 
    471470sS'selectedSchemaIndex' 
    472471p21 
    473472I0 
    474 sS'ShowBalloon' 
     473sS'CutHigh' 
    475474p22 
    476 I1 
     475I0 
    477476sS'Gamma' 
    478477p23 
     
    516515I00 
    517516s.</properties> 
    518         <properties format="pickle" node_id="6">(dp1 
    519 S'widgetShown' 
    520 p2 
    521 NsS'savedWidgetGeometry' 
    522 p3 
    523 NsS'auto_commit' 
    524 p4 
    525 I00 
    526 s.</properties> 
    527517    </node_properties> 
    528518</scheme> 
  • Orange/OrangeCanvas/config.py

    r11299 r11388  
    150150    """ 
    151151    init() 
    152     datadir = QDesktopServices.storageLocation(QDesktopServices.DataLocation) 
    153     datadir = unicode(datadir) 
    154     if not os.path.exists(datadir): 
    155         os.makedirs(datadir) 
    156     return datadir 
     152    cachedir = QDesktopServices.storageLocation(QDesktopServices.CacheLocation) 
     153    cachedir = unicode(cachedir) 
     154    if not os.path.exists(cachedir): 
     155        os.makedirs(cachedir) 
     156    return cachedir 
    157157 
    158158 
  • Orange/OrangeCanvas/document/__init__.py

    r11371 r11390  
    44======== 
    55 
    6 The ``document`` package implements and defines a :class:`SchemeEditWidget` 
     6The :mod:`document` package contains classes for visual interactive editing 
     7of a :class:`Scheme` instance. 
     8 
     9The :class:`.SchemeEditWidget` is the main widget used for editing. It 
     10uses classes defined in :mod:`canvas` to display the scheme. It also 
     11supports undo/redo functionality. 
    712 
    813""" 
  • Orange/OrangeCanvas/document/schemeedit.py

    r11373 r11390  
    11""" 
    2 Scheme Edit widget. 
     2==================== 
     3Scheme Editor Widget 
     4==================== 
     5 
    36 
    47""" 
     
    2831from ..gui.quickhelp import QuickHelpTipEvent 
    2932from ..gui.utils import message_information, disabled 
    30 from ..scheme import scheme 
     33from ..scheme import scheme, SchemeNode, SchemeLink, BaseSchemeAnnotation 
    3134from ..canvas.scene import CanvasScene 
    3235from ..canvas.view import CanvasView 
     
    3538from . import commands 
    3639from . import quickmenu 
     40 
    3741 
    3842log = logging.getLogger(__name__) 
     
    7074class SchemeEditWidget(QWidget): 
    7175    """ 
    72     An editor for a :class:`Scheme` instance. 
     76    A widget for editing a :class:`~.scheme.Scheme` instance. 
    7377 
    7478    """ 
     
    368372        """ 
    369373        Return a list of actions that can be inserted into a toolbar. 
     374        At the moment these are: 
     375 
     376            - 'Zoom' action 
     377            - 'Clean up' action (align to grid) 
     378            - 'New text annotation' action (with a size menu) 
     379            - 'New arrow annotation' action (with a color menu) 
     380 
    370381        """ 
    371382        return [self.__zoomAction, 
     
    376387    def menuBarActions(self): 
    377388        """ 
    378         Return a list of actions that can be inserted into a QMenuBar. 
    379         These actions should have a menu (i.e. a 'File' action should 
    380         have a menu with "New", "Open", ...) 
     389        Return a list of actions that can be inserted into a `QMenuBar`. 
    381390 
    382391        """ 
     
    425434        """ 
    426435        Set quick menu trigger flags. 
     436 
     437        Flags can be a bitwise `or` of: 
     438 
     439            - `SchemeEditWidget.NoTrigeres` 
     440            - `SchemeEditWidget.Clicked` 
     441            - `SchemeEditWidget.DoubleClicked` 
     442            - `SchemeEditWidget.SpaceKey` 
     443            - `SchemeEditWidget.AnyKey` 
     444 
    427445        """ 
    428446        if self.__quickMenuTriggers != triggers: 
     
    478496    def setScheme(self, scheme): 
    479497        """ 
    480         Set the :class:`Scheme` instance to display/edit. 
    481  
     498        Set the :class:`~.scheme.Scheme` instance to display/edit. 
    482499        """ 
    483500        if self.__scheme is not scheme: 
     
    570587    def scheme(self): 
    571588        """ 
    572         Return the :class:`Scheme` edited by the widget. 
     589        Return the :class:`~.scheme.Scheme` edited by the widget. 
    573590        """ 
    574591        return self.__scheme 
     
    584601    def view(self): 
    585602        """ 
    586         Return the class:`QGraphicsView` instance used to display the 
     603        Return the :class:`QGraphicsView` instance used to display the 
    587604        current scene. 
    588605 
     
    602619    def quickMenu(self): 
    603620        """ 
    604         Return a quick menu instance for quick new node creation. 
     621        Return a :class:`~.quickmenu.QuickMenu` popup menu instance for 
     622        new node creation. 
     623 
    605624        """ 
    606625        if self.__quickMenu is None: 
     
    629648    def addNode(self, node): 
    630649        """ 
    631         Add a new node (:class:`SchemeNode`) to the document. 
     650        Add a new node (:class:`.SchemeNode`) to the document. 
    632651        """ 
    633652        command = commands.AddNodeCommand(self.__scheme, node) 
     
    636655    def createNewNode(self, description, title=None, position=None): 
    637656        """ 
    638         Create a new :class:`SchemeNode` and add it to the document. The new 
    639         node is constructed using :ref:`newNodeHelper` method. 
     657        Create a new :class:`.SchemeNode` and add it to the document. 
     658        The new node is constructed using :ref:`newNodeHelper` method. 
    640659 
    641660        """ 
     
    647666    def newNodeHelper(self, description, title=None, position=None): 
    648667        """ 
    649         Return a new initialized :class:`SchemeNode`. If `title` and 
    650         `position` are not supplied they are initialized to sensible 
     668        Return a new initialized :class:`.SchemeNode`. If `title` 
     669        and `position` are not supplied they are initialized to sensible 
    651670        defaults. 
    652671 
     
    658677            position = self.nextPosition() 
    659678 
    660         return scheme.SchemeNode(description, title=title, position=position) 
     679        return SchemeNode(description, title=title, position=position) 
    661680 
    662681    def enumerateTitle(self, title): 
     
    677696    def nextPosition(self): 
    678697        """ 
    679         Return the next default node position (x, y) tuple. This is 
     698        Return the next default node position as a (x, y) tuple. This is 
    680699        a position left of the last added node. 
    681700 
     
    691710    def removeNode(self, node): 
    692711        """ 
    693         Remove a `node` (:class:`SchemeNode`) from the scheme 
     712        Remove a `node` (:class:`.SchemeNode`) from the scheme 
    694713        """ 
    695714        command = commands.RemoveNodeCommand(self.__scheme, node) 
     
    698717    def renameNode(self, node, title): 
    699718        """ 
    700         Rename a `node` (:class:`SchemeNode`) to `title`. 
     719        Rename a `node` (:class:`.SchemeNode`) to `title`. 
    701720        """ 
    702721        command = commands.RenameNodeCommand(self.__scheme, node, title) 
     
    705724    def addLink(self, link): 
    706725        """ 
    707         Add a `link` (:class:`SchemeLink`) to the scheme. 
     726        Add a `link` (:class:`.SchemeLink`) to the scheme. 
    708727        """ 
    709728        command = commands.AddLinkCommand(self.__scheme, link) 
     
    712731    def removeLink(self, link): 
    713732        """ 
    714         Remove a link (:class:`SchemeLink`) from the scheme. 
     733        Remove a link (:class:`.SchemeLink`) from the scheme. 
    715734        """ 
    716735        command = commands.RemoveLinkCommand(self.__scheme, link) 
     
    719738    def addAnnotation(self, annotation): 
    720739        """ 
    721         Add `annotation` (:class:`BaseSchemeAnnotation`) to the scheme 
     740        Add `annotation` (:class:`.BaseSchemeAnnotation`) to the scheme 
    722741        """ 
    723742        command = commands.AddAnnotationCommand(self.__scheme, annotation) 
     
    726745    def removeAnnotation(self, annotation): 
    727746        """ 
    728         Remove `annotation` (:class:`BaseSchemeAnnotation`) from the scheme. 
     747        Remove `annotation` (:class:`.BaseSchemeAnnotation`) from the scheme. 
    729748        """ 
    730749        command = commands.RemoveAnnotationCommand(self.__scheme, annotation) 
     
    805824    def focusNode(self): 
    806825        """ 
    807         Return the current focused :class:`SchemeNode` or ``None`` if no 
     826        Return the current focused :class:`.SchemeNode` or ``None`` if no 
    808827        node has focus. 
    809828 
     
    822841    def selectedNodes(self): 
    823842        """ 
    824         Return all selected :class:`SchemeNode` items. 
     843        Return all selected :class:`.SchemeNode` items. 
    825844        """ 
    826845        return map(self.scene().node_for_item, 
     
    829848    def selectedAnnotations(self): 
    830849        """ 
    831         Return all selected :class:`SchemeAnnotation` items. 
     850        Return all selected :class:`.BaseSchemeAnnotation` items. 
    832851        """ 
    833852        return map(self.scene().annotation_for_item, 
     
    900919            elif etype == QEvent.GraphicsSceneMouseDoubleClick: 
    901920                return self.sceneMouseDoubleClickEvent(event) 
    902             elif etype == QEvent.KeyRelease: 
     921            elif etype == QEvent.KeyPress: 
    903922                return self.sceneKeyPressEvent(event) 
    904923            elif etype == QEvent.KeyRelease: 
     
    9881007                stack.beginMacro(self.tr("Move")) 
    9891008                for scheme_item, (old, new) in self.__itemsMoving.items(): 
    990                     if isinstance(scheme_item, scheme.SchemeNode): 
     1009                    if isinstance(scheme_item, SchemeNode): 
    9911010                        command = commands.MoveNodeCommand( 
    9921011                            self.scheme(), scheme_item, old, new 
    9931012                        ) 
    994                     elif isinstance(scheme_item, scheme.BaseSchemeAnnotation): 
     1013                    elif isinstance(scheme_item, BaseSchemeAnnotation): 
    9951014                        command = commands.AnnotationGeometryChange( 
    9961015                            self.scheme(), scheme_item, old, new 
  • Orange/OrangeCanvas/registry/qt.py

    r11341 r11393  
    249249 
    250250        # This should be inherited from the category. 
     251        background = None 
    251252        if desc.background: 
    252             brush = QBrush(QColor(desc.background)) 
     253            background = desc.background 
    253254        elif category.background: 
    254             brush = QBrush(QColor(category.background)) 
    255         else: 
    256             brush = None 
    257  
    258         if brush is not None: 
     255            background = category.background 
     256 
     257        if background is not None: 
     258            background = NAMED_COLORS.get(background, background) 
     259            brush = QBrush(QColor(background)) 
    259260            item.setData(brush, self.BACKGROUND_ROLE) 
    260261 
  • Orange/OrangeCanvas/scheme/readwrite.py

    r11367 r11391  
    88 
    99from collections import defaultdict 
    10  
    11 import cPickle 
    12  
     10from itertools import chain 
     11 
     12import cPickle as pickle 
     13import json 
     14import pprint 
     15 
     16import ast 
    1317from ast import literal_eval 
    1418 
     
    2630class UnknownWidgetDefinition(Exception): 
    2731    pass 
     32 
     33 
     34def string_eval(source): 
     35    """ 
     36    Evaluate a python string literal `source`. Raise ValueError if 
     37    `source` is not a string literal. 
     38 
     39    >>> string_eval("'a string'") 
     40    a string 
     41 
     42    """ 
     43    node = ast.parse(source, "<source>", mode="eval") 
     44    if not isinstance(node.body, ast.Str): 
     45        raise ValueError("%r is not a string literal" % source) 
     46    return node.body.s 
     47 
     48 
     49def tuple_eval(source): 
     50    """ 
     51    Evaluate a python tuple literal `source` where the elements are 
     52    constrained to be int, float or string. Raise ValueError if not 
     53    a tuple literal. 
     54 
     55    >>> tuple_eval("(1, 2, "3")") 
     56    (1, 2, '3') 
     57 
     58    """ 
     59    node = ast.parse(source, "<source>", mode="eval") 
     60 
     61    if not isinstance(node.body, ast.Tuple): 
     62        raise ValueError("%r is not a tuple literal" % source) 
     63 
     64    if not all(isinstance(el, (ast.Str, ast.Num)) 
     65               for el in node.body.elts): 
     66        raise ValueError("Can only contain numbers or strings") 
     67 
     68    return literal_eval(source) 
     69 
     70 
     71def terminal_eval(source): 
     72    """ 
     73    Evaluate a python 'constant' (string, number, None, True, False) 
     74    `source`. Raise ValueError is not a terminal literal. 
     75 
     76    >>> terminal_eval("True") 
     77    True 
     78 
     79    """ 
     80    node = ast.parse(source, "<source>", mode="eval") 
     81 
     82    try: 
     83        return _terminal_value(node.body) 
     84    except ValueError: 
     85        raise 
     86        raise ValueError("%r is not a terminal constant" % source) 
     87 
     88 
     89def _terminal_value(node): 
     90    if isinstance(node, ast.Str): 
     91        return node.s 
     92    elif isinstance(node, ast.Num): 
     93        return node.n 
     94    elif isinstance(node, ast.Name) and \ 
     95            node.id in ["True", "False", "None"]: 
     96        return __builtins__[node.id] 
     97 
     98    raise ValueError("Not a terminal") 
    2899 
    29100 
     
    46117 
    47118 
    48 def parse_scheme(scheme, stream, error_handler=None): 
     119def parse_scheme(scheme, stream, error_handler=None, 
     120                 allow_pickle_data=False): 
    49121    """ 
    50122    Parse a saved scheme from `stream` and populate a `scheme` 
     
    53125    a 'recoverable' error occurs. By default the exception is simply 
    54126    raised. 
     127 
     128    Parameters 
     129    ---------- 
     130    scheme : :class:`.Scheme` 
     131        A scheme instance to populate with the contents of `stream`. 
     132    stream : file-like object 
     133        A file like object opened for reading. 
     134    error_hander : function, optional 
     135        A function to call with an exception instance when a `recoverable` 
     136        error occurs. 
     137    allow_picked_data : bool, optional 
     138        Specifically allow parsing of picked data streams. 
    55139 
    56140    """ 
     
    70154 
    71155    if version == "1.0": 
    72         parse_scheme_v_1_0(doc, scheme, error_handler=error_handler) 
     156        parse_scheme_v_1_0(doc, scheme, error_handler=error_handler, 
     157                           allow_pickle_data=allow_pickle_data) 
    73158        return scheme 
    74159    else: 
    75         parse_scheme_v_2_0(doc, scheme, error_handler=error_handler) 
     160        parse_scheme_v_2_0(doc, scheme, error_handler=error_handler, 
     161                           allow_pickle_data=allow_pickle_data) 
    76162        return scheme 
    77163 
     
    90176 
    91177    if pos is not None: 
    92         pos = literal_eval(pos) 
     178        pos = tuple_eval(pos) 
    93179 
    94180    return SchemeNode(widget_desc, title=title, position=pos) 
    95181 
    96182 
    97 def parse_scheme_v_2_0(etree, scheme, error_handler, widget_registry=None): 
     183def parse_scheme_v_2_0(etree, scheme, error_handler, widget_registry=None, 
     184                       allow_pickle_data=False): 
    98185    """ 
    99186    Parse an `ElementTree` instance. 
     
    165252 
    166253        if "data" in property_el.attrib: 
    167             data = literal_eval(property_el.attrib.get("data")) 
     254            # data string is 'encoded' with 'repr' i.e. unicode and 
     255            # nonprintable characters are \u or \x escaped. 
     256            # Could use 'codecs' module? 
     257            data = string_eval(property_el.attrib.get("data")) 
    168258        else: 
    169259            data = property_el.text 
    170260 
    171261        properties = None 
    172         try: 
    173             if format != "pickle": 
    174                 raise ValueError("Cannot handle %r format" % format) 
    175  
    176             properties = cPickle.loads(data) 
    177         except Exception: 
    178             log.error("Could not load properties for %r.", node.title, 
    179                       exc_info=True) 
     262        if format != "pickle" or allow_pickle_data: 
     263            try: 
     264                properties = loads(data, format) 
     265            except Exception: 
     266                log.error("Could not load properties for %r.", node.title, 
     267                          exc_info=True) 
    180268 
    181269        if properties is not None: 
     
    186274        if annot_el.tag == "text": 
    187275            rect = annot_el.attrib.get("rect", "(0, 0, 20, 20)") 
    188             rect = literal_eval(rect) 
     276            rect = tuple_eval(rect) 
    189277 
    190278            font_family = annot_el.attrib.get("font-family", "").strip() 
     
    195283                font["family"] = font_family 
    196284            if font_size: 
    197                 font["size"] = literal_eval(font_size) 
     285                font["size"] = int(font_size) 
    198286 
    199287            annot = SchemeTextAnnotation(rect, annot_el.text or "", font=font) 
     
    201289            start = annot_el.attrib.get("start", "(0, 0)") 
    202290            end = annot_el.attrib.get("end", "(0, 0)") 
    203             start, end = map(literal_eval, (start, end)) 
     291            start, end = map(tuple_eval, (start, end)) 
    204292 
    205293            color = annot_el.attrib.get("fill", "red") 
     
    217305 
    218306 
    219 def parse_scheme_v_1_0(etree, scheme, error_handler, widget_registry=None): 
     307def parse_scheme_v_1_0(etree, scheme, error_handler, widget_registry=None, 
     308                       allow_pickle_data=False): 
    220309    """ 
    221310    ElementTree Instance of an old .ows scheme format. 
     
    278367    if settings is not None: 
    279368        data = settings.attrib.get("settingsDictionary", None) 
    280         if data: 
     369        if data and allow_pickle_data: 
    281370            try: 
    282371                properties = literal_eval(data) 
     
    288377        if node.title in properties: 
    289378            try: 
    290                 node.properties = cPickle.loads(properties[node.title]) 
     379                node.properties = pickle.loads(properties[node.title]) 
    291380            except Exception: 
    292381                log.error("Could not unpickle properties for the node %r.", 
     
    307396 
    308397 
    309 def scheme_to_etree(scheme): 
    310     """Return an `xml.etree.ElementTree` representation of the `scheme. 
     398def scheme_to_etree(scheme, data_format="literal", pickle_fallback=False): 
     399    """ 
     400    Return an `xml.etree.ElementTree` representation of the `scheme. 
    311401    """ 
    312402    builder = TreeBuilder(element_factory=Element) 
     
    411501        if node.properties: 
    412502            try: 
    413                 data = cPickle.dumps(node.properties) 
     503                data, format = dumps(node.properties, format=data_format, 
     504                                     pickle_fallback=pickle_fallback) 
    414505            except Exception: 
    415506                log.error("Error serializing properties for node %r", 
     
    418509                builder.start("properties", 
    419510                              {"node_id": str(node_ids[node]), 
    420                                "format": "pickle", 
    421 #                               "data": repr(data), 
    422                                }) 
     511                               "format": format}) 
    423512                builder.data(data) 
    424513                builder.end("properties") 
     
    431520 
    432521 
    433 def scheme_to_ows_stream(scheme, stream, pretty=False): 
    434     """Write scheme to a a stream in Orange Scheme .ows (v 2.0) format. 
    435     """ 
    436     tree = scheme_to_etree(scheme) 
     522def scheme_to_ows_stream(scheme, stream, pretty=False, pickle_fallback=False): 
     523    """ 
     524    Write scheme to a a stream in Orange Scheme .ows (v 2.0) format. 
     525 
     526    Parameters 
     527    ---------- 
     528    scheme : :class:`.Scheme` 
     529        A :class:`.Scheme` instance to serialize. 
     530    stream : file-like object 
     531        A file-like object opened for writing. 
     532    pretty : bool, optional 
     533        If `True` the output xml will be pretty printed (indented). 
     534    pickle_fallback : bool, optional 
     535        If `True` allow scheme node properties to be saves using pickle 
     536        protocol if properties cannot be saved using the default 
     537        notation. 
     538 
     539    """ 
     540    tree = scheme_to_etree(scheme, data_format="literal", 
     541                           pickle_fallback=pickle_fallback) 
    437542 
    438543    if pretty: 
     
    473578 
    474579    return indent_(element, level, True) 
     580 
     581 
     582def dumps(obj, format="literal", prettyprint=False, pickle_fallback=False): 
     583    """ 
     584    Serialize `obj` using `format` ('json' or 'literal') and return its 
     585    string representation and the used serialization format ('literal', 
     586    'json' or 'pickle'). 
     587 
     588    If `pickle_fallback` is True and the serialization with `format` 
     589    fails object's pickle representation will be returned 
     590 
     591    """ 
     592    if format == "literal": 
     593        try: 
     594            return (literal_dumps(obj, prettyprint=prettyprint, indent=1), 
     595                    "literal") 
     596        except (ValueError, TypeError) as ex: 
     597            if not pickle_fallback: 
     598                raise 
     599 
     600            log.warning("Could not serialize to a literal string", 
     601                        exc_info=True) 
     602 
     603    elif format == "json": 
     604        try: 
     605            return (json.dumps(obj, indent=1 if prettyprint else None), 
     606                    "json") 
     607        except (ValueError, TypeError): 
     608            if not pickle_fallback: 
     609                raise 
     610 
     611            log.warning("Could not serialize to a json string", 
     612                        exc_info=True) 
     613 
     614    elif format == "pickle": 
     615        return pickle.dumps(obj), "pickle" 
     616 
     617    else: 
     618        raise ValueError("Unsupported format %r" % format) 
     619 
     620    if pickle_fallback: 
     621        log.warning("Using pickle fallback") 
     622        return pickle.dumps(obj), "pickle" 
     623    else: 
     624        raise Exception("Something strange happened.") 
     625 
     626 
     627def loads(string, format): 
     628    if format == "literal": 
     629        return literal_eval(string) 
     630    elif format == "json": 
     631        return json.loads(string) 
     632    elif format == "pickle": 
     633        return pickle.loads(string) 
     634    else: 
     635        raise ValueError("Unknown format") 
     636 
     637 
     638# This is a subset of PyON serialization. 
     639def literal_dumps(obj, prettyprint=False, indent=4): 
     640    """ 
     641    Write obj into a string as a python literal. 
     642    """ 
     643    memo = {} 
     644    NoneType = type(None) 
     645 
     646    def check(obj): 
     647        if type(obj) in [int, long, float, bool, NoneType, unicode, str]: 
     648            return True 
     649 
     650        if id(obj) in memo: 
     651            raise ValueError("{0} is a recursive structure".format(obj)) 
     652 
     653        memo[id(obj)] = obj 
     654 
     655        if type(obj) in [list, tuple]: 
     656            return all(map(check, obj)) 
     657        elif type(obj) is dict: 
     658            return all(map(check, chain(obj.iterkeys(), obj.itervalues()))) 
     659        else: 
     660            raise TypeError("{0} can not be serialized as a python " 
     661                             "literal".format(type(obj))) 
     662 
     663    check(obj) 
     664 
     665    if prettyprint: 
     666        return pprint.pformat(obj, indent=indent) 
     667    else: 
     668        return repr(obj) 
     669 
     670 
     671literal_loads = literal_eval 
  • Orange/OrangeCanvas/scheme/scheme.py

    r11367 r11391  
    545545        self.annotation_removed.emit(annotation) 
    546546 
    547     def save_to(self, stream, pretty=True): 
     547    def clear(self): 
     548        """ 
     549        Remove all nodes, links, and annotation items from the scheme. 
     550        """ 
     551        def is_terminal(node): 
     552            return not bool(self.find_links(source_node=node)) 
     553 
     554        while self.nodes: 
     555            terminal_nodes = filter(is_terminal, self.nodes) 
     556            for node in terminal_nodes: 
     557                self.remove_node(node) 
     558 
     559        for annotation in self.annotations: 
     560            self.remove_annotation(annotation) 
     561 
     562        assert(not (self.nodes or self.links or self.annotations)) 
     563 
     564    def save_to(self, stream, pretty=True, pickle_fallback=False): 
    548565        """ 
    549566        Save the scheme as an xml formated file to `stream` 
     567 
     568        See also 
     569        -------- 
     570        .scheme_to_ows_stream 
     571 
    550572        """ 
    551573        if isinstance(stream, basestring): 
    552574            stream = open(stream, "wb") 
    553575 
    554         scheme_to_ows_stream(self, stream, pretty) 
     576        scheme_to_ows_stream(self, stream, pretty, 
     577                             pickle_fallback=pickle_fallback) 
    555578 
    556579    def load_from(self, stream): 
  • Orange/OrangeCanvas/scheme/tests/test_readwrite.py

    r11112 r11391  
    77               SchemeArrowAnnotation, SchemeTextAnnotation 
    88 
     9from .. import readwrite 
    910from ..readwrite import scheme_to_ows_stream, parse_scheme 
    1011 
     
    7374                self.assertEqual(annot1.start_pos, annot2.start_pos) 
    7475                self.assertEqual(annot1.end_pos, annot2.end_pos) 
     76 
     77    def test_safe_evals(self): 
     78        s = readwrite.string_eval(r"'\x00\xff'") 
     79        self.assertEquals(s, chr(0) + chr(255)) 
     80 
     81        with self.assertRaises(ValueError): 
     82            readwrite.string_eval("[1, 2]") 
     83 
     84        t = readwrite.tuple_eval("(1, 2.0, 'a')") 
     85        self.assertEqual(t, (1, 2.0, 'a')) 
     86 
     87        with self.assertRaises(ValueError): 
     88            readwrite.tuple_eval("u'string'") 
     89 
     90        with self.assertRaises(ValueError): 
     91            readwrite.tuple_eval("(1, [1, [2, ]])") 
     92 
     93        self.assertIs(readwrite.terminal_eval("True"), True) 
     94        self.assertIs(readwrite.terminal_eval("False"), False) 
     95        self.assertIs(readwrite.terminal_eval("None"), None) 
     96 
     97        self.assertEqual(readwrite.terminal_eval("42"), 42) 
     98        self.assertEqual(readwrite.terminal_eval("'42'"), '42') 
     99 
     100    def test_literal_dump(self): 
     101        struct = {1: [{(1, 2): ""}], 
     102                  True: 1.0, 
     103                  None: None} 
     104 
     105        s = readwrite.literal_dumps(struct) 
     106        self.assertEqual(readwrite.literal_loads(s), struct) 
     107 
     108        with self.assertRaises(ValueError): 
     109            recur = [1] 
     110            recur.append(recur) 
     111            readwrite.literal_dumps(recur) 
     112 
     113        with self.assertRaises(TypeError): 
     114            readwrite.literal_dumps(self) 
  • Orange/OrangeCanvas/scheme/widgetsscheme.py

    r11297 r11391  
    181181        return changed 
    182182 
    183     def save_to(self, stream): 
     183    def save_to(self, stream, pretty=True, pickle_fallback=False): 
    184184        self.sync_node_properties() 
    185         Scheme.save_to(self, stream) 
     185        Scheme.save_to(self, stream, pretty, pickle_fallback) 
    186186 
    187187    def __on_help_request(self): 
  • docs/canvas/index.rst

    r11369 r11390  
    1111   registry 
    1212   canvas 
     13   document 
Note: See TracChangeset for help on using the changeset viewer.