Ignore:
Files:
13 added
7 deleted
100 edited

Legend:

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

    r11557 r11598  
    399399                    ) 
    400400 
     401        self.open_and_freeze_action = \ 
     402            QAction(self.tr("Open and Freeze"), self, 
     403                    objectName="action-open-and-freeze", 
     404                    toolTip=self.tr("Open a new scheme and freeze signal " 
     405                                    "propagation."), 
     406                    triggered=self.open_and_freeze_scheme 
     407                    ) 
     408 
    401409        self.save_action = \ 
    402410            QAction(self.tr("Save"), self, 
     
    576584        file_menu.addAction(self.new_action) 
    577585        file_menu.addAction(self.open_action) 
     586        file_menu.addAction(self.open_and_freeze_action) 
    578587        file_menu.addAction(self.reload_last_action) 
    579588 
     
    881890        else: 
    882891            return QDialog.Rejected 
     892 
     893    def open_and_freeze_scheme(self): 
     894        """ 
     895        Open a new scheme and freeze signal propagation. Return 
     896        QDialog.Rejected if the user canceled the operation and 
     897        QDialog.Accepted otherwise. 
     898 
     899        """ 
     900        frozen = self.freeze_action.isChecked() 
     901        if not frozen: 
     902            self.freeze_action.trigger() 
     903 
     904        state = self.open_scheme() 
     905        if state == QDialog.Rejected: 
     906            # If the action was rejected restore the original frozen state 
     907            if not frozen: 
     908                self.freeze_action.trigger() 
     909        return state 
    883910 
    884911    def open_scheme_file(self, filename): 
  • Orange/OrangeCanvas/canvas/items/nodeitem.py

    r11525 r11614  
    2222from .utils import saturated, radial_gradient 
    2323 
     24from ...scheme.node import UserMessage 
    2425from ...registry import NAMED_COLORS 
    2526from ...resources import icon_loader 
     
    979980        """ 
    980981        pass 
     982 
     983    def setStateMessage(self, message): 
     984        """ 
     985        Set a state message to display over the item. 
     986 
     987        Parameters 
     988        ---------- 
     989        message : UserMessage 
     990            Message to display. `message.severity` is used to determine 
     991            the icon and `message.contents` is used as a tool tip. 
     992 
     993        """ 
     994        # TODO: Group messages by message_id not by severity 
     995        # and deprecate set[Error|Warning|Error]Message 
     996        if message.severity == UserMessage.Info: 
     997            self.setInfoMessage(message.contents) 
     998        elif message.severity == UserMessage.Warning: 
     999            self.setWarningMessage(message.contents) 
     1000        elif message.severity == UserMessage.Error: 
     1001            self.setErrorMessage(message.contents) 
    9811002 
    9821003    def setErrorMessage(self, message): 
  • Orange/OrangeCanvas/canvas/scene.py

    r11558 r11614  
    333333        node.progress_changed.connect(item.setProgress) 
    334334        node.processing_state_changed.connect(item.setProcessingState) 
     335        node.state_message_changed.connect(item.setStateMessage) 
    335336 
    336337        return self.add_node_item(item) 
     
    388389        node.progress_changed.disconnect(item.setProgress) 
    389390        node.processing_state_changed.disconnect(item.setProcessingState) 
     391        node.state_message_changed.disconnect(item.setStateMessage) 
    390392 
    391393        self.remove_node_item(item) 
  • Orange/OrangeCanvas/canvas/view.py

    r11238 r11616  
    44import logging 
    55 
    6 from PyQt4.QtGui import QGraphicsView, QCursor 
    7 from PyQt4.QtCore import Qt, QRect, QRectF, QTimer 
     6from PyQt4.QtGui import QGraphicsView, QCursor, QIcon 
     7from PyQt4.QtCore import Qt, QRect, QSize, QRectF, QPoint, QTimer 
    88 
    99log = logging.getLogger(__name__) 
     
    1111 
    1212class CanvasView(QGraphicsView): 
    13     """Canvas View handles the zooming and panning. 
     13    """Canvas View handles the zooming. 
    1414    """ 
    1515 
     
    1717        QGraphicsView.__init__(self, *args) 
    1818        self.setAlignment(Qt.AlignTop | Qt.AlignLeft) 
     19 
     20        self.__backgroundIcon = QIcon() 
    1921 
    2022        self.__autoScroll = False 
     
    113115 
    114116        log.debug("Auto scroll advance") 
     117 
     118    def setBackgroundIcon(self, icon): 
     119        if not isinstance(icon, QIcon): 
     120            raise TypeError("A QIcon expected.") 
     121 
     122        if self.__backgroundIcon != icon: 
     123            self.__backgroundIcon = icon 
     124            self.viewport().update() 
     125 
     126    def backgroundIcon(self): 
     127        return QIcon(self.__backgroundIcon) 
     128 
     129    def drawBackground(self, painter, rect): 
     130        QGraphicsView.drawBackground(self, painter, rect) 
     131 
     132        if not self.__backgroundIcon.isNull(): 
     133            painter.setClipRect(rect) 
     134            vrect = QRect(QPoint(0, 0), self.viewport().size()) 
     135            vrect = self.mapToScene(vrect).boundingRect() 
     136 
     137            pm = self.__backgroundIcon.pixmap( 
     138                vrect.size().toSize().boundedTo(QSize(200, 200)) 
     139            ) 
     140            pmrect = QRect(QPoint(0, 0), pm.size()) 
     141            pmrect.moveCenter(vrect.center().toPoint()) 
     142            if rect.toRect().intersects(pmrect): 
     143                painter.drawPixmap(pmrect, pm) 
  • Orange/OrangeCanvas/document/schemeedit.py

    r11550 r11658  
    2020    QKeySequence, QUndoStack, QGraphicsItem, QGraphicsObject, 
    2121    QGraphicsTextItem, QCursor, QFont, QPainter, QPixmap, QColor, 
    22     QIcon, QWhatsThisClickedEvent 
     22    QIcon, QWhatsThisClickedEvent, QBrush 
    2323) 
    2424 
     
    3232from ..gui.quickhelp import QuickHelpTipEvent 
    3333from ..gui.utils import message_information, disabled 
    34 from ..scheme import scheme, SchemeNode, SchemeLink, BaseSchemeAnnotation 
     34from ..scheme import ( 
     35    scheme, signalmanager, SchemeNode, SchemeLink, BaseSchemeAnnotation 
     36) 
    3537from ..canvas.scene import CanvasScene 
    3638from ..canvas.view import CanvasView 
     
    128130        self.__undoStack.cleanChanged[bool].connect(self.__onCleanChanged) 
    129131 
    130         # OWBaseWidget properties when set to clean state 
    131         self.__cleanSettings = [] 
     132        # scheme node properties when set to a clean state 
     133        self.__cleanProperties = [] 
    132134 
    133135        self.__editFinishedMapper = QSignalMapper(self) 
     
    480482 
    481483        if not modified: 
    482             self.__cleanSettings = self.__scheme.widget_settings() 
     484            self.__cleanProperties = node_properties(self.__scheme) 
    483485            self.__undoStack.setClean() 
    484486        else: 
    485             self.__cleanSettings = [] 
     487            self.__cleanProperties = [] 
    486488 
    487489    modified = Property(bool, fget=isModified, fset=setModified) 
     
    489491    def isModifiedStrict(self): 
    490492        """ 
    491         Is the document modified. Run a strict check against all node 
    492         properties as they were at the time when the last call to 
    493         `setModified(True)` was made. 
    494  
    495         """ 
    496         settingsChanged = self.__cleanSettings != \ 
    497                           self.__scheme.widget_settings() 
     493        Is the document modified. 
     494 
     495        Run a strict check against all node properties as they were 
     496        at the time when the last call to `setModified(True)` was made. 
     497 
     498        """ 
     499        propertiesChanged = self.__cleanProperties != \ 
     500                            node_properties(self.__scheme) 
    498501 
    499502        log.debug("Modified strict check (modified flag: %s, " 
     
    501504                  self.__modified, 
    502505                  self.__undoStack.isClean(), 
    503                   settingsChanged) 
    504  
    505         return self.isModified() or settingsChanged 
     506                  propertiesChanged) 
     507 
     508        return self.isModified() or propertiesChanged 
    506509 
    507510    def setQuickMenuTriggers(self, triggers): 
     
    589592            if self.__scheme: 
    590593                self.__scheme.title_changed.disconnect(self.titleChanged) 
    591                 self.__scheme.node_added.disconnect(self.__onNodeAdded) 
    592                 self.__scheme.node_removed.disconnect(self.__onNodeRemoved) 
    593594                self.__scheme.removeEventFilter(self) 
     595                sm = self.__scheme.findChild(signalmanager.SignalManager) 
     596                if sm: 
     597                    sm.stateChanged.disconnect( 
     598                        self.__signalManagerStateChanged) 
    594599 
    595600            self.__scheme = scheme 
     
    599604            if self.__scheme: 
    600605                self.__scheme.title_changed.connect(self.titleChanged) 
    601                 self.__scheme.node_added.connect(self.__onNodeAdded) 
    602                 self.__scheme.node_removed.connect(self.__onNodeRemoved) 
    603606                self.titleChanged.emit(scheme.title) 
    604                 self.__cleanSettings = scheme.widget_settings() 
     607                self.__cleanProperties = node_properties(scheme) 
     608                sm = scheme.findChild(signalmanager.SignalManager) 
     609                if sm: 
     610                    sm.stateChanged.connect(self.__signalManagerStateChanged) 
    605611            else: 
    606                 self.__cleanSettings = [] 
     612                self.__cleanProperties = [] 
    607613 
    608614            self.__teardownScene(self.__scene) 
     
    619625 
    620626            if self.__scheme: 
    621                 for node in self.__scheme.nodes: 
    622                     self.__onNodeAdded(node) 
    623  
    624627                self.__scheme.installEventFilter(self) 
    625628 
     
    12291232            QCoreApplication.sendEvent(self, ev) 
    12301233 
    1231     def __onNodeAdded(self, node): 
    1232         widget = self.__scheme.widget_for_node[node] 
    1233         widget.widgetStateChanged.connect(self.__onWidgetStateChanged) 
    1234  
    1235     def __onNodeRemoved(self, node): 
    1236         widget = self.__scheme.widget_for_node[node] 
    1237         widget.widgetStateChanged.disconnect(self.__onWidgetStateChanged) 
    1238  
    1239     def __onWidgetStateChanged(self, *args): 
    1240         widget = self.sender() 
    1241         self.scheme() 
    1242         widget_to_node = dict(reversed(item) for item in \ 
    1243                               self.__scheme.widget_for_node.items()) 
    1244         node = widget_to_node[widget] 
    1245         item = self.__scene.item_for_node(node) 
    1246  
    1247         info = widget.widgetStateToHtml(True, False, False) 
    1248         warning = widget.widgetStateToHtml(False, True, False) 
    1249         error = widget.widgetStateToHtml(False, False, True) 
    1250  
    1251         item.setInfoMessage(info or None) 
    1252         item.setWarningMessage(warning or None) 
    1253         item.setErrorMessage(error or None) 
    1254  
    12551234    def __onNodeActivate(self, item): 
    12561235        node = self.__scene.node_for_item(item) 
    1257         widget = self.scheme().widget_for_node[node] 
     1236        widget = self.scheme().widget_for_node(node) 
    12581237        widget.show() 
    12591238        widget.raise_() 
     
    15541533            self.__scene.setFont(font) 
    15551534 
     1535    def __signalManagerStateChanged(self, state): 
     1536        if state == signalmanager.SignalManager.Running: 
     1537            self.__view.setBackgroundBrush(QBrush(Qt.NoBrush)) 
     1538#            self.__view.setBackgroundIcon(QIcon()) 
     1539        elif state == signalmanager.SignalManager.Paused: 
     1540            self.__view.setBackgroundBrush(QBrush(QColor(235, 235, 235))) 
     1541#            self.__view.setBackgroundIcon(QIcon("canvas_icons:Pause.svg")) 
     1542 
    15561543 
    15571544def geometry_from_annotation_item(item): 
     
    15931580    """ 
    15941581    return unicodedata.category(unichar) not in _control 
     1582 
     1583 
     1584def node_properties(scheme): 
     1585    scheme.sync_node_properties() 
     1586    return [dict(node.properties) for node in scheme.nodes] 
  • Orange/OrangeCanvas/main.py

    r11535 r11596  
    181181                log.info("%r style sheet not found.", stylesheet) 
    182182 
    183     if stylesheet_string is not None: 
    184         app.setStyleSheet(stylesheet_string) 
    185  
    186183    # Add the default canvas_icons search path 
    187184    dirpath = os.path.abspath(os.path.dirname(OrangeCanvas.__file__)) 
     
    189186 
    190187    canvas_window = CanvasMainWindow() 
     188 
     189    if stylesheet_string is not None: 
     190        canvas_window.setStyleSheet(stylesheet_string) 
    191191 
    192192    if not options.force_discovery: 
  • Orange/OrangeCanvas/orngCanvas.pyw

    r11024 r11654  
    847847        lastRefresh = self.settings["lastAddonsRefresh"] 
    848848        dlg = orngDlgs.AddOnManagerDialog(self, self) 
    849         if t - lastRefresh > 7*24*3600 or Orange.utils.addons.addons_corrupted: 
     849        if t - lastRefresh > 7*24*3600 or Orange.utils.addons.addons_corrupted(): 
    850850            dlg.show() 
    851             if Orange.utils.addons.addons_corrupted or \ 
     851            if Orange.utils.addons.addons_corrupted() or \ 
    852852               QMessageBox.question(self, "Refresh", 
    853853                                    "List of add-ons in repository has not been refreshed for more than a week. Do you want to download the list now?", 
  • Orange/OrangeCanvas/orngDlgs.py

    r11479 r11654  
    958958            lastRefresh = self.loadtimefn() 
    959959        t = time.time() 
    960         if t - lastRefresh > 7*24*3600 or Orange.utils.addons.addons_corrupted: 
    961             if Orange.utils.addons.addons_corrupted or \ 
     960        if t - lastRefresh > 7*24*3600 or Orange.utils.addons.addons_corrupted(): 
     961            if Orange.utils.addons.addons_corrupted() or \ 
    962962               QMessageBox.question(self, "Refresh", 
    963963                                    "List of available add-ons has not been refreshed for more than a week. Do you want to download the list now?", 
  • Orange/OrangeCanvas/preview/previewbrowser.py

    r11308 r11623  
    1919from PyQt4.QtCore import pyqtSignal as Signal 
    2020 
    21 from ..scheme.utils import check_type 
     21from ..utils import check_type 
    2222from ..gui.dropshadow import DropShadowFrame 
    2323from . import previewmodel 
  • Orange/OrangeCanvas/registry/discovery.py

    r11368 r11663  
    102102            try: 
    103103                if isinstance(point, types.ModuleType): 
    104                     if point.__path__: 
     104                    if hasattr(point, "__path__"): 
    105105                        # Entry point is a package (a widget category) 
    106106                        self.process_category_package( 
  • Orange/OrangeCanvas/scheme/__init__.py

    r11419 r11623  
    2323 
    2424from .errors import * 
    25  
    26 from . import utils 
  • Orange/OrangeCanvas/scheme/annotations.py

    r11367 r11623  
    1010from PyQt4.QtCore import pyqtProperty as Property 
    1111 
    12 from .utils import check_type 
     12from ..utils import check_type 
    1313 
    1414 
  • Orange/OrangeCanvas/scheme/link.py

    r11419 r11623  
    1010from PyQt4.QtCore import pyqtProperty as Property 
    1111 
    12 from .utils import name_lookup 
     12from ..utils import name_lookup 
    1313from .errors import IncompatibleChannelTypeError 
    1414 
  • Orange/OrangeCanvas/scheme/node.py

    r11419 r11614  
    99from PyQt4.QtCore import pyqtSignal as Signal 
    1010from PyQt4.QtCore import pyqtProperty as Property 
     11 
     12 
     13class UserMessage(object): 
     14    """ 
     15    A user message that should be displayed in a scheme view. 
     16 
     17    Paramaters 
     18    ---------- 
     19    contents : str 
     20        Message text. 
     21    severity : int 
     22        Message severity. 
     23    message_id : A hashable object 
     24        Message id. 
     25    data : dict 
     26        A dictionary with optional extra data. 
     27 
     28    """ 
     29    #: Severity flags 
     30    Info, Warning, Error = 1, 2, 3 
     31 
     32    def __init__(self, contents, severity=Info, message_id=None, data={}): 
     33        self.contents = contents 
     34        self.severity = severity 
     35        self.message_id = message_id 
     36        self.data = dict(data) 
    1137 
    1238 
     
    4268        self.__progress = -1 
    4369        self.__processing_state = 0 
     70        self.__state_messages = {} 
    4471        self.properties = properties or {} 
    4572 
     
    167194                        fget=tool_tip) 
    168195 
     196    def set_state_message(self, message): 
     197        """ 
     198        Set a message to be displayed by a scheme view for this node. 
     199        """ 
     200        if message.message_id in self.__state_messages and \ 
     201                not message.contents: 
     202            del self.__state_messages[message.message_id] 
     203 
     204        self.__state_messages[message.message_id] = message 
     205 
     206        self.state_message_changed.emit(message) 
     207 
     208    #: The node's state message has changed 
     209    state_message_changed = Signal(UserMessage) 
     210 
    169211    def __str__(self): 
    170212        return u"SchemeNode(description_id=%s, title=%r, ...)" % \ 
  • Orange/OrangeCanvas/scheme/scheme.py

    r11419 r11657  
    2121from .annotations import BaseSchemeAnnotation 
    2222 
    23 from .utils import check_arg, check_type 
     23from ..utils import check_arg, check_type 
    2424 
    2525from .errors import ( 
     
    597597        assert(not (self.nodes or self.links or self.annotations)) 
    598598 
     599    def sync_node_properties(self): 
     600        """ 
     601        Called before saving, allowing a subclass to update/sync. 
     602 
     603        The default implementation does nothing. 
     604 
     605        """ 
     606        pass 
     607 
    599608    def save_to(self, stream, pretty=True, pickle_fallback=False): 
    600609        """ 
     
    609618            stream = open(stream, "wb") 
    610619 
     620        self.sync_node_properties() 
     621 
    611622        scheme_to_ows_stream(self, stream, pretty, 
    612623                             pickle_fallback=pickle_fallback) 
  • Orange/OrangeCanvas/scheme/signalmanager.py

    r11467 r11639  
    169169 
    170170    def on_node_removed(self, node): 
    171         # remove all pending input signals for node so we don't get stale 
    172         # references in process_node 
    173         # NOTE: This does not remove output signals this node. In particular 
    174         # the final 'None' values might be left on the queue. 
     171        # remove all pending input signals for node so we don't get 
     172        # stale references in process_node. 
     173        # NOTE: This does not remove output signals for this node. In 
     174        # particular the final 'None' will be delivered to the sink 
     175        # nodes even after the source node is no longer in the scheme. 
    175176        log.info("Node %r removed. Removing pending signals.", 
    176177                 node.title) 
  • Orange/OrangeCanvas/scheme/widgetsscheme.py

    r11563 r11659  
    2121 
    2222import sip 
    23 from PyQt4.QtGui import QShortcut, QKeySequence, QWhatsThisClickedEvent 
    24 from PyQt4.QtCore import Qt, QCoreApplication, QEvent, SIGNAL 
     23from PyQt4.QtGui import ( 
     24    QShortcut, QKeySequence, QWhatsThisClickedEvent, QWidget 
     25) 
     26 
     27from PyQt4.QtCore import Qt, QObject, QCoreApplication, QEvent, SIGNAL 
     28from PyQt4.QtCore import pyqtSignal as Signal 
    2529 
    2630from .signalmanager import SignalManager, compress_signals, can_enable_dynamic 
    2731from .scheme import Scheme, SchemeNode 
    28 from .utils import name_lookup, check_arg, check_type 
     32from .node import UserMessage 
     33from ..utils import name_lookup 
    2934from ..resources import icon_loader 
    30 from ..config import rc 
    3135 
    3236log = logging.getLogger(__name__) 
     
    4751        Scheme.__init__(self, parent, title, description) 
    4852 
    49         self.widgets = [] 
    50         self.widget_for_node = {} 
    51         self.node_for_widget = {} 
    5253        self.signal_manager = WidgetsSignalManager(self) 
    53         self.signal_manager.processingStarted[SchemeNode].connect( 
    54             self.__on_processing_started 
    55         ) 
    56         self.signal_manager.processingFinished[SchemeNode].connect( 
    57             self.__on_processing_finished 
    58         ) 
    59  
    60     def add_node(self, node): 
    61         """ 
    62         Add a `SchemeNode` instance to the scheme and create/initialize the 
    63         OWBaseWidget instance for it. 
    64  
    65         """ 
    66         check_arg(node not in self.nodes, "Node already in scheme.") 
    67         check_type(node, SchemeNode) 
    68  
    69         # Create the widget before a call to Scheme.add_node in 
    70         # case someone connected to node_added already expects 
    71         # widget_for_node, etc. to be up to date. 
    72         widget = self.create_widget_instance(node) 
    73         Scheme.add_node(self, node) 
    74  
    75         self.widgets.append(widget) 
    76  
    77     def remove_node(self, node): 
    78         Scheme.remove_node(self, node) 
    79         widget = self.widget_for_node[node] 
    80  
    81         self.signal_manager.on_node_removed(node) 
    82  
    83         del self.widget_for_node[node] 
    84         del self.node_for_widget[widget] 
    85  
    86         widget.close() 
    87  
    88         # Save settings to user global settings. 
    89         if not widget._settingsFromSchema: 
    90             widget.saveSettings() 
    91  
    92         # Notify the widget it will be deleted. 
    93         widget.onDeleteWidget() 
    94         # And schedule it for deletion. 
    95         widget.deleteLater() 
    96  
    97     def add_link(self, link): 
    98         Scheme.add_link(self, link) 
    99         self.signal_manager.link_added(link) 
    100  
    101     def remove_link(self, link): 
    102         Scheme.remove_link(self, link) 
    103         self.signal_manager.link_removed(link) 
    104  
    105     def create_widget_instance(self, node): 
    106         """ 
    107         Create a OWBaseWidget instance for the node. 
    108         """ 
    109         desc = node.description 
    110         klass = name_lookup(desc.qualified_name) 
    111  
    112         log.info("Creating %r instance.", klass) 
    113         widget = klass.__new__( 
    114             klass, 
    115             _owInfo=rc.get("canvas.show-state-info", True), 
    116             _owWarning=rc.get("canvas.show-state-warning", True), 
    117             _owError=rc.get("canvas.show-state-error", True), 
    118             _owShowStatus=rc.get("OWWidget.show-status", True), 
    119             _useContexts=rc.get("OWWidget.use-contexts", True), 
    120             _category=desc.category, 
    121             _settingsFromSchema=node.properties 
    122         ) 
    123  
    124         # Add the node/widget mapping s before calling __init__ 
    125         # Some OWWidgets might already send data in the constructor 
    126         # (should this be forbidden? Raise a warning?) 
    127         self.signal_manager.on_node_added(node) 
    128  
    129         self.widget_for_node[node] = widget 
    130         self.node_for_widget[widget] = node 
    131  
    132         widget.__init__(None, self.signal_manager) 
    133         widget.setCaption(node.title) 
    134         widget.widgetInfo = desc 
    135  
    136         widget.setWindowIcon( 
    137             icon_loader.from_description(desc).get(desc.icon) 
    138         ) 
    139  
    140         widget.setVisible(node.properties.get("visible", False)) 
    141  
    142         node.title_changed.connect(widget.setCaption) 
    143  
    144         # Bind widgets progress/processing state back to the node's properties 
    145         widget.progressBarValueChanged.connect(node.set_progress) 
    146         widget.processingStateChanged.connect(node.set_processing_state) 
    147         self.connect(widget, 
    148                      SIGNAL("blockingStateChanged(bool)"), 
    149                      self.signal_manager._update) 
    150  
    151         # Install a help shortcut on the widget 
    152         help_shortcut = QShortcut(QKeySequence("F1"), widget) 
    153         help_shortcut.activated.connect(self.__on_help_request) 
    154         return widget 
    155  
    156     def widget_settings(self): 
    157         """Return a list of dictionaries with widget settings. 
    158         """ 
    159         return [self.widget_for_node[node].getSettings(alsoContexts=False) 
    160                 for node in self.nodes] 
     54        self.widget_manager = WidgetManager(self) 
     55        self.widget_manager.set_scheme(self) 
     56 
     57    def widget_for_node(self, node): 
     58        """ 
     59        Return the OWWidget instance for a `node` 
     60        """ 
     61        return self.widget_manager.widget_for_node(node) 
     62 
     63    def node_for_widget(self, widget): 
     64        """ 
     65        Return the SchemeNode instance for the `widget`. 
     66        """ 
     67        return self.widget_manager.node_for_widget(widget) 
    16168 
    16269    def sync_node_properties(self): 
    163         """Sync the widget settings/properties with the SchemeNode.properties. 
     70        """ 
     71        Sync the widget settings/properties with the SchemeNode.properties. 
    16472        Return True if there were any changes in the properties (i.e. if the 
    16573        new node.properties differ from the old value) and False otherwise. 
    16674 
    167         .. note:: this should hopefully be removed in the feature, when the 
    168             widget can notify a changed setting property. 
    169  
    17075        """ 
    17176        changed = False 
    17277        for node in self.nodes: 
    173             widget = self.widget_for_node[node] 
    174             settings = widget.getSettings(alsoContexts=False) 
     78            widget = self.widget_for_node(node) 
     79            settings = widget.getSettings(alsoContexts=True) 
    17580            if settings != node.properties: 
    17681                node.properties = settings 
     
    17984        return changed 
    18085 
    181     def save_to(self, stream, pretty=True, pickle_fallback=False): 
    182         self.sync_node_properties() 
    183         Scheme.save_to(self, stream, pretty, pickle_fallback) 
    184  
    185     def event(self, event): 
    186         """ 
    187         Reimplemented from `QObject.event`. 
    188  
    189         Responds to QEvent.Close event by stopping signal processing and 
    190         closing all widgets. 
    191  
    192         """ 
    193         if event.type() == QEvent.Close: 
    194             self.signal_manager.stop() 
     86 
     87class WidgetManager(QObject): 
     88    """ 
     89    OWWidget instance manager class. 
     90 
     91    This class handles the lifetime of OWWidget instances in a 
     92    :class:`WidgetsScheme`. 
     93 
     94    """ 
     95    #: A new OWWidget was created and added by the manager. 
     96    widget_for_node_added = Signal(SchemeNode, QWidget) 
     97 
     98    #: An OWWidget was removed, hidden and will be deleted when appropriate. 
     99    widget_for_node_removed = Signal(SchemeNode, QWidget) 
     100 
     101    #: Widget processing state flags: 
     102    #:   * InputUpdate - signal manager is updating/setting the 
     103    #:     widget's inputs 
     104    #:   * BlockingUpdate - widget has entered a blocking state 
     105    #:   * ProcessingUpdate - widget has entered processing state 
     106    InputUpdate, BlockingUpdate, ProcessingUpdate = 1, 2, 4 
     107 
     108    def __init__(self, parent): 
     109        QObject.__init__(self, parent) 
     110        self.__scheme = None 
     111        self.__signal_manager = None 
     112        self.__widgets = [] 
     113        self.__widget_for_node = {} 
     114        self.__node_for_widget = {} 
     115 
     116        # Widgets that were 'removed' from the scheme but were at 
     117        # the time in an input update loop and could not be deleted 
     118        # immediately 
     119        self.__delay_delete = set() 
     120 
     121        # processing state flags for all nodes (including the ones 
     122        # in __delay_delete). 
     123        self.__widget_processing_state = {} 
     124 
     125        # Tracks the widget in the update loop by the SignalManager 
     126        self.__updating_widget = None 
     127 
     128    def set_scheme(self, scheme): 
     129        """ 
     130        Set the :class:`WidgetsScheme` instance to manage. 
     131        """ 
     132        self.__scheme = scheme 
     133        self.__signal_manager = scheme.findChild(SignalManager) 
     134 
     135        self.__signal_manager.processingStarted[SchemeNode].connect( 
     136            self.__on_processing_started 
     137        ) 
     138        self.__signal_manager.processingFinished[SchemeNode].connect( 
     139            self.__on_processing_finished 
     140        ) 
     141        scheme.node_added.connect(self.add_widget_for_node) 
     142        scheme.node_removed.connect(self.remove_widget_for_node) 
     143        scheme.installEventFilter(self) 
     144 
     145    def scheme(self): 
     146        """ 
     147        Return the scheme instance on which this manager is installed. 
     148        """ 
     149        return self.__scheme 
     150 
     151    def signal_manager(self): 
     152        """ 
     153        Return the signal manager in use on the :func:`scheme`. 
     154        """ 
     155        return self.__signal_manager 
     156 
     157    def widget_for_node(self, node): 
     158        """ 
     159        Return the OWWidget instance for the scheme node. 
     160        """ 
     161        return self.__widget_for_node[node] 
     162 
     163    def node_for_widget(self, widget): 
     164        """ 
     165        Return the SchemeNode instance for the OWWidget. 
     166 
     167        Raise a KeyError if the widget does not map to a node in the scheme. 
     168        """ 
     169        return self.__node_for_widget[widget] 
     170 
     171    def add_widget_for_node(self, node): 
     172        """ 
     173        Create a new OWWidget instance for the corresponding scheme node. 
     174        """ 
     175        widget = self.create_widget_instance(node) 
     176 
     177        self.__widgets.append(widget) 
     178        self.__widget_for_node[node] = widget 
     179        self.__node_for_widget[widget] = node 
     180 
     181        self.widget_for_node_added.emit(node, widget) 
     182 
     183    def remove_widget_for_node(self, node): 
     184        """ 
     185        Remove the OWWidget instance for node. 
     186        """ 
     187        widget = self.widget_for_node(node) 
     188 
     189        self.__widgets.remove(widget) 
     190        del self.__widget_for_node[node] 
     191        del self.__node_for_widget[widget] 
     192 
     193        self.widget_for_node_removed.emit(node, widget) 
     194 
     195        self._delete_widget(widget) 
     196 
     197    def _delete_widget(self, widget): 
     198        """ 
     199        Delete the OWBaseWidget instance. 
     200        """ 
     201        widget.close() 
     202 
     203        # Save settings to user global settings. 
     204        if not widget._settingsFromSchema: 
     205            widget.saveSettings() 
     206 
     207        # Notify the widget it will be deleted. 
     208        widget.onDeleteWidget() 
     209 
     210        if self.__widget_processing_state[widget] != 0: 
     211            # If the widget is in an update loop and/or blocking we 
     212            # delay the scheduled deletion until the widget is done. 
     213            self.__delay_delete.add(widget) 
     214        else: 
     215            widget.deleteLater() 
     216 
     217    def create_widget_instance(self, node): 
     218        """ 
     219        Create a OWWidget instance for the node. 
     220        """ 
     221        desc = node.description 
     222        klass = name_lookup(desc.qualified_name) 
     223 
     224        log.info("Creating %r instance.", klass) 
     225        widget = klass.__new__( 
     226            klass, 
     227            _owInfo=True, 
     228            _owWarning=True, 
     229            _owError=True, 
     230            _owShowStatus=True, 
     231            _useContexts=True, 
     232            _category=desc.category, 
     233            _settingsFromSchema=node.properties 
     234        ) 
     235 
     236        # Init the node/widget mapping and state before calling __init__ 
     237        # Some OWWidgets might already send data in the constructor 
     238        # (should this be forbidden? Raise a warning?) triggering the signal 
     239        # manager which would request the widget => node mapping or state 
     240        self.__widget_for_node[node] = widget 
     241        self.__node_for_widget[widget] = node 
     242        self.__widget_processing_state[widget] = 0 
     243 
     244        widget.__init__(None, self.signal_manager()) 
     245        widget.setCaption(node.title) 
     246        widget.widgetInfo = desc 
     247 
     248        widget.setWindowIcon( 
     249            icon_loader.from_description(desc).get(desc.icon) 
     250        ) 
     251 
     252        widget.setVisible(node.properties.get("visible", False)) 
     253 
     254        node.title_changed.connect(widget.setCaption) 
     255 
     256        # Widget's info/warning/error messages. 
     257        widget.widgetStateChanged.connect(self.__on_widget_state_changed) 
     258 
     259        # Widget's progress bar value state. 
     260        widget.progressBarValueChanged.connect(node.set_progress) 
     261 
     262        # Widget processing state (progressBarInit/Finished) 
     263        # and the blocking state. 
     264        widget.processingStateChanged.connect( 
     265            self.__on_processing_state_changed 
     266        ) 
     267        self.connect(widget, 
     268                     SIGNAL("blockingStateChanged(bool)"), 
     269                     self.__on_blocking_state_changed) 
     270 
     271        # Install a help shortcut on the widget 
     272        help_shortcut = QShortcut(QKeySequence("F1"), widget) 
     273        help_shortcut.activated.connect(self.__on_help_request) 
     274 
     275        return widget 
     276 
     277    def node_processing_state(self, node): 
     278        """ 
     279        Return the processing state flags for the node. 
     280 
     281        Same as `manager.node_processing_state(manger.widget_for_node(node))` 
     282 
     283        """ 
     284        widget = self.widget_for_node(node) 
     285        return self.__widget_processing_state[widget] 
     286 
     287    def widget_processing_state(self, widget): 
     288        """ 
     289        Return the processing state flags for the widget. 
     290 
     291        The state is an bitwise or of `InputUpdate` and `BlockingUpdate`. 
     292 
     293        """ 
     294        return self.__widget_processing_state[widget] 
     295 
     296    def eventFilter(self, receiver, event): 
     297        if receiver is self.__scheme and event.type() == QEvent.Close: 
     298            self.signal_manager().stop() 
    195299 
    196300            # Notify the widget instances. 
    197             for widget in self.widget_for_node.values(): 
     301            for widget in self.__widget_for_node.values(): 
    198302                widget.close() 
    199303 
     
    206310            event.accept() 
    207311            return True 
    208         else: 
    209             return Scheme.event(self, event) 
     312 
     313        return QObject.eventFilter(self, receiver, event) 
    210314 
    211315    def __on_help_request(self): 
    212316        """ 
    213         Help shortcut was pressed. We send a `QWhatsThisClickedEvent` and 
    214         hope someone responds to it. 
     317        Help shortcut was pressed. We send a `QWhatsThisClickedEvent` to 
     318        the scheme and hope someone responds to it. 
    215319 
    216320        """ 
    217321        # Sender is the QShortcut, and parent the OWBaseWidget 
    218322        widget = self.sender().parent() 
    219         node = self.node_for_widget.get(widget) 
    220         if node: 
     323        try: 
     324            node = self.node_for_widget(widget) 
     325        except KeyError: 
     326            pass 
     327        else: 
    221328            url = "help://search?id={0}".format(node.description.id) 
    222329            event = QWhatsThisClickedEvent(url) 
    223             QCoreApplication.sendEvent(self, event) 
     330            QCoreApplication.sendEvent(self.scheme(), event) 
     331 
     332    def __on_widget_state_changed(self, message_type, message_id, 
     333                                  message_value): 
     334        """ 
     335        The OWBaseWidget info/warning/error state has changed. 
     336 
     337        message_type is one of "Info", "Warning" or "Error" string depending 
     338        of which method (information, warning, error) was called. message_id 
     339        is the first int argument if supplied, and message_value the message 
     340        text. 
     341 
     342        """ 
     343        widget = self.sender() 
     344        try: 
     345            node = self.node_for_widget(widget) 
     346        except KeyError: 
     347            pass 
     348        else: 
     349            message_type = str(message_type) 
     350            if message_type == "Info": 
     351                contents = widget.widgetStateToHtml(True, False, False) 
     352                level = UserMessage.Info 
     353            elif message_type == "Warning": 
     354                contents = widget.widgetStateToHtml(False, True, False) 
     355                level = UserMessage.Warning 
     356            elif message_type == "Error": 
     357                contents = widget.widgetStateToHtml(False, False, True) 
     358                level = UserMessage.Error 
     359            else: 
     360                raise ValueError("Invalid message_type: %r" % message_type) 
     361 
     362            if not contents: 
     363                contents = None 
     364 
     365            message = UserMessage(contents, severity=level, 
     366                                  message_id=message_type, 
     367                                  data={"content-type": "text/html"}) 
     368            node.set_state_message(message) 
     369 
     370    def __on_processing_state_changed(self, state): 
     371        """ 
     372        A widget processing state has changed (progressBarInit/Finished) 
     373        """ 
     374        widget = self.sender() 
     375        try: 
     376            node = self.node_for_widget(widget) 
     377        except KeyError: 
     378            return 
     379 
     380        if state: 
     381            self.__widget_processing_state[widget] |= self.ProcessingUpdate 
     382        else: 
     383            self.__widget_processing_state[widget] &= ~self.ProcessingUpdate 
     384        self.__update_node_processing_state(node) 
    224385 
    225386    def __on_processing_started(self, node): 
    226         node.set_processing_state(1) 
     387        """ 
     388        Signal manager entered the input update loop for the node. 
     389        """ 
     390        widget = self.widget_for_node(node) 
     391        # Remember the widget instance. The node and the node->widget mapping 
     392        # can be removed between this and __on_processing_finished. 
     393        self.__updating_widget = widget 
     394        self.__widget_processing_state[widget] |= self.InputUpdate 
     395        self.__update_node_processing_state(node) 
    227396 
    228397    def __on_processing_finished(self, node): 
    229         node.set_processing_state(0) 
     398        """ 
     399        Signal manager exited the input update loop for the node. 
     400        """ 
     401        widget = self.__updating_widget 
     402        self.__widget_processing_state[widget] &= ~self.InputUpdate 
     403 
     404        if widget in self.__node_for_widget: 
     405            self.__update_node_processing_state(node) 
     406        elif widget in self.__delay_delete: 
     407            self.__try_delete(widget) 
     408        else: 
     409            raise ValueError("%r is not managed" % widget) 
     410 
     411        self.__updating_widget = None 
     412 
     413    def __on_blocking_state_changed(self, state): 
     414        """ 
     415        OWWidget blocking state has changed. 
     416        """ 
     417        if not state: 
     418            # schedule an update pass. 
     419            self.signal_manager()._update() 
     420 
     421        widget = self.sender() 
     422        if state: 
     423            self.__widget_processing_state[widget] |= self.BlockingUpdate 
     424        else: 
     425            self.__widget_processing_state[widget] &= ~self.BlockingUpdate 
     426 
     427        if widget in self.__node_for_widget: 
     428            node = self.node_for_widget(widget) 
     429            self.__update_node_processing_state(node) 
     430 
     431        elif widget in self.__delay_delete: 
     432            self.__try_delete(widget) 
     433 
     434    def __update_node_processing_state(self, node): 
     435        """ 
     436        Update the `node.processing_state` to reflect the widget state. 
     437        """ 
     438        state = self.node_processing_state(node) 
     439        node.set_processing_state(1 if state else 0) 
     440 
     441    def __try_delete(self, widget): 
     442        if self.__widget_processing_state[widget] == 0: 
     443            self.__delay_delete.remove(widget) 
     444            widget.deleteLater() 
     445            del self.__widget_processing_state[widget] 
    230446 
    231447 
    232448class WidgetsSignalManager(SignalManager): 
     449    """ 
     450    A signal manager for a WidgetsScheme. 
     451    """ 
    233452    def __init__(self, scheme): 
    234453        SignalManager.__init__(self, scheme) 
    235454 
    236455        scheme.installEventFilter(self) 
    237         # We keep a mapping from node->widget after the node/widget has been 
    238         # removed from the scheme until we also process all the outgoing signal 
    239         # updates. The reason is the old OWBaseWidget's MULTI channel protocol 
    240         # where the actual source widget instance is passed to the signal 
    241         # handler, and in the delayed update the mapping in `scheme()` is no 
    242         # longer available. 
    243         self._widget_backup = {} 
    244         self._widgets_to_delete = set() 
    245         self._active_node = None 
     456 
    246457        self.freezing = 0 
    247458 
    248459        self.__scheme_deleted = False 
     460 
    249461        scheme.destroyed.connect(self.__on_scheme_destroyed) 
    250  
    251     def on_node_removed(self, node): 
    252         widget = self.scheme().widget_for_node[node] 
    253  
    254         assert not self.scheme().find_links(sink_node=node), \ 
    255             "Node removed but still has input links" 
    256  
    257         signals = self.compress_signals(self.pending_input_signals(node)) 
    258         if not all(signal.value is None for signal in signals): 
    259             log.error("Non 'None' signals pending for a removed node %r", 
    260                          node.title) 
    261  
    262         SignalManager.on_node_removed(self, node) 
    263  
    264         if self.runtime_state() == SignalManager.Processing and \ 
    265                 node is self._active_node or self.is_blocking(node): 
    266             # Delay the widget delete until it finishes. 
    267             # Keep a reference to the widget and install a filter. 
    268             self._widgets_to_delete.add(widget) 
    269             widget.installEventFilter(self) 
    270  
    271         # Store the node->widget mapping for possible delayed signal id. 
    272         # It will be removed in `process_queued` when all signals 
    273         # originating from this widget are delivered. 
    274         self._widget_backup[node] = widget 
    275  
    276     def send(self, widget, channelname, value, id): 
     462        scheme.node_added.connect(self.on_node_added) 
     463        scheme.node_removed.connect(self.on_node_removed) 
     464        scheme.link_added.connect(self.link_added) 
     465        scheme.link_removed.connect(self.link_removed) 
     466 
     467    def send(self, widget, channelname, value, signal_id): 
    277468        """ 
    278469        send method compatible with OWBaseWidget. 
    279470        """ 
    280471        scheme = self.scheme() 
    281  
    282         if widget not in scheme.node_for_widget: 
    283             # The Node/Widget was already removed from the scheme 
     472        try: 
     473            node = scheme.node_for_widget(widget) 
     474        except KeyError: 
     475            # The Node/Widget was already removed from the scheme. 
     476            log.debug("Node for %r is not in the scheme.", widget) 
    284477            return 
    285  
    286         node = scheme.node_for_widget[widget] 
    287478 
    288479        try: 
     
    293484            return 
    294485 
    295         SignalManager.send(self, node, channel, value, id) 
     486        # Expand the signal_id with the unique widget id and the 
     487        # channel name. This is needed for OWBaseWidget's input 
     488        # handlers (Multiple flag). 
     489        signal_id = (widget.widgetId, channelname, signal_id) 
     490 
     491        SignalManager.send(self, node, channel, value, signal_id) 
    296492 
    297493    def is_blocking(self, node): 
    298         return self.scheme().widget_for_node[node].isBlocking() 
     494        return self.scheme().widget_manager.node_processing_state(node) != 0 
    299495 
    300496    def send_to_node(self, node, signals): 
    301497        """ 
    302         Implementation of `SignalManager.send_to_node`. Deliver data signals 
    303         to OWBaseWidget instance. 
    304  
    305         """ 
    306         if node in self.scheme().widget_for_node: 
    307             widget = self.scheme().widget_for_node[node] 
    308         else: 
    309             widget = self._widget_backup[node] 
    310  
    311         self._active_node = node 
     498        Implementation of `SignalManager.send_to_node`. 
     499 
     500        Deliver input signals to an OWBaseWidget instance. 
     501 
     502        """ 
     503        widget = self.scheme().widget_for_node(node) 
    312504        self.process_signals_for_widget(node, widget, signals) 
    313         self._active_node = None 
    314  
    315         if widget in self._widgets_to_delete: 
    316             # If this node/widget was removed during the 
    317             # 'process_signals_for_widget' 
    318             self._widgets_to_delete.remove(widget) 
    319             widget.deleteLater() 
    320505 
    321506    def compress_signals(self, signals): 
     507        """ 
     508        Reimplemented from :func:`SignalManager.compress_signals`. 
     509        """ 
    322510        return compress_signals(signals) 
    323511 
    324     def process_queued(self, max_nodes=None): 
    325         SignalManager.process_queued(self, max_nodes=max_nodes) 
    326  
    327         # Remove node->widgets backup mapping no longer needed. 
    328         nodes_removed = set(self._widget_backup.keys()) 
    329         sources_remaining = set(signal.link.source_node for 
    330                                 signal in self._input_queue) 
    331  
    332         nodes_to_remove = nodes_removed - sources_remaining 
    333         for node in nodes_to_remove: 
    334             del self._widget_backup[node] 
    335  
    336512    def process_signals_for_widget(self, node, widget, signals): 
    337513        """ 
    338         Process new signals for a OWBaseWidget. 
     514        Process new signals for the OWBaseWidget. 
    339515        """ 
    340516        # This replaces the old OWBaseWidget.processSignals method 
     
    348524            widget.processingHandler(widget, 1) 
    349525 
    350         scheme = self.scheme() 
    351526        app = QCoreApplication.instance() 
    352527 
     
    371546                args = (value,) 
    372547            else: 
    373                 source_node = link.source_node 
    374                 source_name = link.source_channel.name 
    375  
    376                 if source_node in scheme.widget_for_node: 
    377                     source_widget = scheme.widget_for_node[source_node] 
    378                 else: 
    379                     # Node is no longer in the scheme. 
    380                     source_widget = self._widget_backup[source_node] 
    381  
    382                 # The old OWBaseWidget.processSignals sends the source widget 
    383                 # instance along. 
    384                 # TODO: Does any widget actually use it, or could it be 
    385                 # removed (replaced with a unique id)? 
    386                 args = (value, (source_widget, source_name, signal.id)) 
     548                args = (value, signal.id) 
    387549 
    388550            log.debug("Process signals: calling %s.%s (from %s with id:%s)", 
     
    409571            app.restoreOverrideCursor() 
    410572 
    411         # TODO: Test if async processing works, then remove this 
    412         while widget.isBlocking(): 
    413             self.thread().msleep(50) 
    414             app.processEvents() 
    415  
    416573        if widget.processingHandler: 
    417574            widget.processingHandler(widget, 0) 
     
    475632            Construct SignalLink from an SchemeLink. 
    476633            """ 
    477             w1 = scheme.widget_for_node[link.source_node] 
    478             w2 = scheme.widget_for_node[link.sink_node] 
     634            w1 = scheme.widget_for_node(link.source_node) 
     635            w2 = scheme.widget_for_node(link.sink_node) 
    479636 
    480637            # Input/OutputSignal are reused from description. Interface 
     
    561718                self.__scheme_deleted = True 
    562719                return True 
    563         elif receiver in self._widgets_to_delete and \ 
    564                 event.type() == QEvent.DeferredDelete: 
    565             if self._widget_backup.get(self._active_node, None) is receiver: 
    566                 # The widget is still being updated. We need to keep it alive, 
    567                 # it will be deleted in `send_to_node`. 
    568                 log.info("Deferring a 'DeferredDelete' until widget exits " 
    569                          "the 'process_signals_for_widget'.") 
    570                 event.setAccepted(False) 
    571                 return True 
    572720 
    573721        return SignalManager.eventFilter(self, receiver, event) 
  • Orange/OrangeCanvas/utils/__init__.py

    r11247 r11623  
    11from .qtcompat import sip_getapi, toPyObject 
     2 
     3 
     4def dotted_getattr(obj, name): 
     5    """ 
     6    `getattr` like function accepting a dotted name for attribute lookup. 
     7    """ 
     8    return reduce(getattr, name.split("."), obj) 
     9 
     10 
     11def qualified_name(obj): 
     12    """ 
     13    Return a qualified name for `obj` (type or function). 
     14    """ 
     15    if obj.__name__ == "__builtin__": 
     16        return obj.__name__ 
     17    else: 
     18        return "%s.%s" % (obj.__module__, obj.__name__) 
     19 
     20 
     21def name_lookup(qualified_name): 
     22    """ 
     23    Return the object referenced by a qualified name (dotted name). 
     24    """ 
     25    if "." not in qualified_name: 
     26        qualified_name = "__builtin__." + qualified_name 
     27 
     28    module_name, class_name = qualified_name.rsplit(".", 1) 
     29    module = __import__(module_name, fromlist=[class_name]) 
     30    return getattr(module, class_name) 
     31 
     32 
     33def asmodule(module): 
     34    """ 
     35    Return the :class:`module` instance named by `module`. 
     36 
     37    If `module` is already a module instance and not a string, return 
     38    it unchanged. 
     39 
     40    """ 
     41    if isinstance(module, str): 
     42        module = __import__(module, fromlist=[]) 
     43    return module 
     44 
     45 
     46def check_type(obj, type_or_tuple): 
     47    if not isinstance(obj, type_or_tuple): 
     48        raise TypeError("Expected %r. Got %r" % (type_or_tuple, type(obj))) 
     49 
     50 
     51def check_subclass(cls, class_or_tuple): 
     52    if not issubclass(cls, class_or_tuple): 
     53        raise TypeError("Expected %r. Got %r" % (class_or_tuple, type(cls))) 
     54 
     55 
     56def check_arg(pred, value): 
     57    if not pred: 
     58        raise ValueError(value) 
  • Orange/OrangeWidgets/Classify/OWClassificationTreeGraph.py

    r11216 r11629  
    253253 
    254254        OWGUI.rubber(self.NodeTab) 
    255          
    256255 
    257256    def sendReport(self): 
    258257        if self.tree: 
    259             tclass = self.tree.examples.domain.classVar.values[self.TargetClassIndex] 
     258            tclass = str(self.targetCombo.currentText()) 
    260259            tsize = "%i nodes, %i leaves" % (orngTree.countNodes(self.tree), orngTree.countLeaves(self.tree)) 
    261260        else: 
     
    336335        self.scene.update() 
    337336        self.sceneView.repaint() 
    338          
     337 
    339338    def toggleNodeColor(self): 
     339        palette = self.scene.colorPalette 
    340340        for node in self.scene.nodes(): 
    341             if self.NodeColorMethod == 0:   # default 
     341            dist = node.tree.distribution 
     342            if self.NodeColorMethod == 0: 
     343                # default color 
    342344                color = BodyColor_Default 
    343             elif self.NodeColorMethod == 1: # instances in node 
    344                 div = self.tree.distribution.cases 
    345                 if div > 1e-6: 
    346                     light = 400 - 300*node.tree.distribution.cases/div 
    347                 else: 
    348                     light = 100 
     345            elif self.NodeColorMethod == 1: 
     346                # number of instances in node 
     347                all_cases = self.tree.distribution.cases 
     348                light = 200 - 100 * dist.cases / (all_cases or 1) 
    349349                color = BodyCasesColor_Default.light(light) 
    350             elif self.NodeColorMethod == 2: # majority class probability 
    351                 light=400- 300*float(node.majorityCount) / node.tree.distribution.abs 
    352                 color = self.scene.colorPalette[node.tree.examples.domain.classVar.values.index(node.majorityClass)].light(light) 
    353             elif self.NodeColorMethod == 3: # target class probability 
    354                 div = node.tree.distribution.cases 
    355                 if div > 1e-6: 
    356                     light=400-300*node.tree.distribution[self.TargetClassIndex]/div 
    357                 else: 
    358                     light = 100 
    359                 color = self.scene.colorPalette[self.TargetClassIndex].light(light) 
    360             elif self.NodeColorMethod == 4: # target class distribution 
    361                 div = self.tree.distribution[self.TargetClassIndex] 
    362                 if div > 1e-6: 
    363                     light=200 - 100*node.tree.distribution[self.TargetClassIndex]/div 
    364                 else: 
    365                     light = 100 
    366                 color = self.scene.colorPalette[self.TargetClassIndex].light(light) 
    367 #            gradient = QLinearGradient(0, 0, 0, 100) 
    368 #                gradient.setStops([(0, QColor(Qt.gray).lighter(120)), (1, QColor(Qt.lightGray).lighter())]) 
    369 #            gradient.setStops([(0, color), (1, color.lighter())]) 
    370 #            node.backgroundBrush = QBrush(gradient) 
     350            elif self.NodeColorMethod == 2: 
     351                # majority class probability 
     352                modus = dist.modus() 
     353                p = dist[modus] / (dist.abs or 1) 
     354                light = 400 - 300 * p 
     355                color = palette[int(modus)].light(light) 
     356            elif self.NodeColorMethod == 3: 
     357                # target class probability 
     358                p = dist[self.TargetClassIndex] / (dist.cases or 1) 
     359                light = 200 - 100 * p 
     360                color = palette[self.TargetClassIndex].light(light) 
     361            elif self.NodeColorMethod == 4: 
     362                # target class distribution 
     363                all_target = self.tree.distribution[self.TargetClassIndex] or 1 
     364                light = 200 - 100 * dist[self.TargetClassIndex] / all_target 
     365                color = palette[self.TargetClassIndex].light(light) 
    371366            node.backgroundBrush = QBrush(color) 
    372367        self.scene.update() 
     
    384379        self.scene.update() 
    385380 
    386     def ctree(self, tree=None): 
     381    def ctree(self, classifier=None): 
     382        """ 
     383        Set the input TreeClassifier. 
     384        """ 
    387385        self.send("Data", None) 
    388386        self.closeContext() 
    389387        self.targetCombo.clear() 
    390         if tree: 
    391             for name in tree.tree.examples.domain.classVar.values: 
     388        self.classifier = classifier 
     389        if classifier: 
     390            for name in classifier.domain.classVar.values: 
    392391                self.targetCombo.addItem(name) 
    393             self.TargetClassIndex=0 
    394             self.openContext("", tree.domain) 
     392            self.TargetClassIndex = 0 
     393            self.openContext("", classifier.domain) 
    395394        else: 
    396395            self.openContext("", None) 
    397         OWTreeViewer2D.ctree(self, tree) 
     396        OWTreeViewer2D.ctree(self, classifier) 
    398397        self.togglePies() 
    399398 
  • Orange/OrangeWidgets/Classify/OWRandomForest.py

    r11096 r11628  
    1313from orngWrap import PreprocessedLearner 
    1414 
     15 
    1516class OWRandomForest(OWWidget): 
    16     settingsList = ["name", "trees", "attributes", "attributesP", "preNodeInst", "preNodeInstP", "limitDepth", "limitDepthP", "rseed"] 
    17  
    18     def __init__(self, parent=None, signalManager = None, name='Random Forest'): 
    19         OWWidget.__init__(self, parent, signalManager, name, wantMainArea=False, resizingEnabled=False) 
     17    settingsList = ["name", "trees", "attributes", "attributesP", 
     18                    "preNodeInst", "preNodeInstP", "limitDepth", 
     19                    "limitDepthP", "rseed"] 
     20 
     21    def __init__(self, parent=None, signalManager=None, name='Random Forest'): 
     22        OWWidget.__init__(self, parent, signalManager, name, 
     23                          wantMainArea=False, resizingEnabled=False) 
    2024 
    2125        self.inputs = [("Data", ExampleTable, self.setData), 
    22                        ("Preprocess", PreprocessedLearner, self.setPreprocessor)] 
    23          
     26                       ("Preprocess", PreprocessedLearner, 
     27                        self.setPreprocessor)] 
     28 
    2429        self.outputs = [("Learner", orange.Learner), 
    25                         ("Random Forest Classifier", orange.Classifier)] 
     30                        ("Random Forest Classifier", orange.Classifier), 
     31                        ("Selected Tree", Orange.classification.tree.TreeClassifier)] 
    2632 
    2733        self.name = 'Random Forest' 
     
    3440        self.limitDepthP = 3 
    3541        self.rseed = 0 
     42        self.outtree = 0 
    3643 
    3744        self.maxTrees = 10000 
     
    4249        self.preprocessor = None 
    4350 
    44         OWGUI.lineEdit(self.controlArea, self, 'name', box='Learner/Classifier Name', tooltip='Name to be used by other widgets to identify your learner/classifier.') 
     51        OWGUI.lineEdit(self.controlArea, self, 'name', 
     52                       box='Learner/Classifier Name', 
     53                       tooltip='Name to be used by other widgets to identify ' 
     54                               'your learner/classifier.') 
    4555 
    4656        OWGUI.separator(self.controlArea) 
     
    4858        self.bBox = OWGUI.widgetBox(self.controlArea, 'Basic Properties') 
    4959 
    50         self.treesBox = OWGUI.spin(self.bBox, self, "trees", 1, self.maxTrees, orientation="horizontal", label="Number of trees in forest") 
    51         self.attributesBox, self.attributesPBox = OWGUI.checkWithSpin(self.bBox, self, "Consider exactly", 1, 10000, "attributes", "attributesP", " "+"random attributes at each split.") 
    52         self.rseedBox = OWGUI.spin(self.bBox, self, "rseed", 0, 100000, orientation="horizontal", label="Seed for random generator ") 
     60        self.treesBox = OWGUI.spin(self.bBox, self, "trees", 1, self.maxTrees, 
     61                                   orientation="horizontal", 
     62                                   label="Number of trees in forest") 
     63        self.attributesBox, self.attributesPBox = \ 
     64            OWGUI.checkWithSpin(self.bBox, self, "Consider exactly", 
     65                                1, 10000, "attributes", "attributesP", 
     66                                " random attributes at each split.") 
     67 
     68        self.rseedBox = OWGUI.spin(self.bBox, self, "rseed", 0, 100000, 
     69                                   orientation="horizontal", 
     70                                   label="Seed for random generator ") 
    5371 
    5472        OWGUI.separator(self.controlArea) 
     
    5674        self.pBox = OWGUI.widgetBox(self.controlArea, 'Growth Control') 
    5775 
    58         self.limitDepthBox, self.limitDepthPBox = OWGUI.checkWithSpin(self.pBox, self, "Maximal depth of individual trees", 1, 1000, "limitDepth", "limitDepthP", "") 
    59         self.preNodeInstBox, self.preNodeInstPBox = OWGUI.checkWithSpin(self.pBox, self, "Stop splitting nodes with ", 1, 1000, "preNodeInst", "preNodeInstP", " or fewer instances") 
    60  
    61         OWGUI.separator(self.controlArea) 
    62  
    63         OWGUI.separator(self.controlArea) 
    64  
    65         self.btnApply = OWGUI.button(self.controlArea, self, "&Apply Changes", callback = self.doBoth, disabled=0, default=True) 
    66  
    67         self.resize(100,200) 
     76        self.limitDepthBox, self.limitDepthPBox = \ 
     77            OWGUI.checkWithSpin(self.pBox, self, 
     78                                "Maximal depth of individual trees", 
     79                                1, 1000, "limitDepth", "limitDepthP", "") 
     80 
     81        self.preNodeInstBox, self.preNodeInstPBox = \ 
     82            OWGUI.checkWithSpin(self.pBox, self, "Stop splitting nodes with ", 
     83                                1, 1000, "preNodeInst", "preNodeInstP", 
     84                                " or fewer instances") 
     85 
     86        OWGUI.separator(self.controlArea) 
     87 
     88        self.streesBox = OWGUI.spin(self.controlArea, self, "outtree", -1, 
     89                                    self.maxTrees, 
     90                                    orientation="horizontal", 
     91                                    label="Index of tree on the output", 
     92                                    callback=[self.period, self.extree]) 
     93        self.streeEnabled(False) 
     94 
     95        OWGUI.separator(self.controlArea) 
     96 
     97        self.btnApply = OWGUI.button(self.controlArea, self, 
     98                                     "&Apply Changes", 
     99                                     callback=self.doBoth, 
     100                                     disabled=0, 
     101                                     default=True) 
     102 
     103        self.resize(100, 200) 
    68104 
    69105        self.setLearner() 
     
    71107    def sendReport(self): 
    72108        self.reportSettings("Learning parameters", 
    73                             [("Number of trees", self.trees), 
    74                              ("Considered number of attributes at each split", self.attributeP if self.attributes else "not set"), 
    75                              ("Seed for random generator", self.rseed), 
    76                              ("Maximal depth of individual trees", self.limitDepthP if self.limitDepth else "not set"), 
    77                              ("Minimal number of instances in a leaf", self.preNodeInstP if self.preNodeInst else "not limited") 
    78                            ]) 
     109                    [("Number of trees", self.trees), 
     110                     ("Considered number of attributes at each split", 
     111                      self.attributeP if self.attributes else "not set"), 
     112                     ("Seed for random generator", self.rseed), 
     113                     ("Maximal depth of individual trees", 
     114                      self.limitDepthP if self.limitDepth else "not set"), 
     115                     ("Minimal number of instances in a leaf", 
     116                      self.preNodeInstP if self.preNodeInst else "not limited") 
     117                   ]) 
    79118        self.reportData(self.data) 
    80119 
     
    87126 
    88127        from Orange.classification.tree import SimpleTreeLearner 
    89          
     128 
    90129        smallLearner = SimpleTreeLearner() 
    91130 
    92131        if self.preNodeInst: 
    93             smallLearner.min_instances = self.preNodeInstP  
     132            smallLearner.min_instances = self.preNodeInstP 
    94133        else: 
    95134            smallLearner.min_instances = 0 
    96135 
    97136        if self.limitDepth: 
    98             smallLearner.max_depth = self.limitDepthP  
    99          
    100         learner = orngEnsemble.RandomForestLearner(base_learner=smallLearner,  
     137            smallLearner.max_depth = self.limitDepthP 
     138 
     139        learner = orngEnsemble.RandomForestLearner(base_learner=smallLearner, 
    101140                            trees=self.trees, rand=rand, attributes=attrs) 
    102141 
     
    119158 
    120159    def setData(self, data): 
    121         self.data = self.isDataWithClass(data, orange.VarTypes.Discrete, checkMissing=True) and data or None 
    122          
     160        if not self.isDataWithClass(data, orange.VarTypes.Discrete, 
     161                                    checkMissing=True): 
     162            data = None 
     163        self.data = data 
     164 
    123165        #self.setLearner() 
    124  
     166        self.streeEnabled(False) 
    125167        if self.data: 
    126168            learner = self.constructLearner() 
    127             pb = OWGUI.ProgressBar(self, iterations=self.trees) 
    128169            self.progressBarInit() 
    129170            learner.callback = lambda v: self.progressBarSet(100.0 * v) 
    130171            try: 
    131172                self.classifier = learner(self.data) 
     173                self.streeEnabled(True) 
    132174                self.classifier.name = self.name 
    133175            except Exception, (errValue): 
     
    139181 
    140182        self.send("Random Forest Classifier", self.classifier) 
    141          
     183 
    142184    def setPreprocessor(self, pp): 
    143185        self.preprocessor = pp 
     
    148190        self.setData(self.data) 
    149191 
    150  
    151  
    152 ############################################################################## 
    153 # Test the widget, run from DOS prompt 
    154 # > python OWDataTable.py) 
    155 # Make sure that a sample data set (adult_sample.tab) is in the directory 
    156  
    157 if __name__=="__main__": 
    158     a=QApplication(sys.argv) 
    159     ow=OWRandomForest() 
    160     a.setMainWidget(ow) 
     192    def period(self): 
     193        if self.outtree == -1: 
     194            self.outtree = self.claTrees - 1 
     195        elif self.outtree >= self.claTrees: 
     196            self.outtree = 0 
     197 
     198    def extree(self): 
     199        stc = self.classifier.classifiers[self.outtree] 
     200        if self.preprocessor: 
     201            # TODO: get the transformed data at learning step from the 
     202            # wrapped learner (or at least cache it here) 
     203            train_data = self.data.translate(self.classifier.domain) 
     204        else: 
     205            train_data = self.data 
     206 
     207        # Replay the bootstrap sampling as done by RandomForestLearner 
     208        rand = random.Random(self.claSeed) 
     209        n = len(train_data) 
     210        selection = [rand.randrange(n) 
     211                     for _ in range((self.outtree + 1) * n)] 
     212        # need the last n samples 
     213        selection = selection[-n:] 
     214        train_data = train_data.get_items_ref(selection) 
     215 
     216        tree = Orange.classification.tree._simple_tree_convert( 
     217            stc, self.classifier.domain, train_data) 
     218 
     219        self.send("Selected Tree", tree) 
     220 
     221    def streeEnabled(self, status): 
     222        if status: 
     223            self.claTrees = self.trees 
     224            self.claSeed = self.rseed 
     225            self.streesBox.setDisabled(False) 
     226            self.period() 
     227            self.extree() 
     228        else: 
     229            self.streesBox.setDisabled(True) 
     230 
     231 
     232if __name__ == "__main__": 
     233    a = QApplication(sys.argv) 
     234    ow = OWRandomForest() 
    161235 
    162236    d = orange.ExampleTable('adult_sample') 
     
    164238 
    165239    ow.show() 
    166     a.exec_loop() 
     240    a.exec_() 
    167241    ow.saveSettings() 
  • Orange/OrangeWidgets/Data/icons/SelectAttributes.svg

    r11217 r11638  
    11<?xml version="1.0" encoding="utf-8"?> 
    2 <!-- Generator: Adobe Illustrator 16.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> 
     2<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> 
    33<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 
    44<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" 
     
    66<rect x="30" y="32" fill="#FFFFFF" width="10" height="8"/> 
    77<g> 
    8     <path fill="#333333" d="M6,6v36h36V6H6z M40,14v8v1v8v9H29H19h-1H8v-8v-1v-8v-1v-8v0h10l0,0h1l0,0h10l0,0h1l0,0L40,14L40,14z"/> 
     8    <path fill="#333333" d="M6,6v36h36V6H6z M40,14v8v1v8v9H29H19h-1H8v-8v-1v-8v-1v-8l0,0h10l0,0h1l0,0h10l0,0h1l0,0H40L40,14z"/> 
    99    <polygon fill="#666666" points="19,31 19,23 19,22 19,14 18,14 18,22 8,22 8,23 18,23 18,31 8,31 8,32 18,32 18,40 19,40 19,32      
    1010        "/> 
     
    1212    <rect x="8" y="32" fill="#FFFFFF" width="10" height="8"/> 
    1313    <rect x="8" y="23" fill="#FFFFFF" width="10" height="8"/> 
    14     <rect x="8" y="14" fill="#FFFFFF" width="10" height="0"/> 
     14    <polygon fill="#FFFFFF" points="8,14 18,14 18,14    "/> 
    1515    <rect x="8" y="14" fill="#FFFFFF" width="10" height="8"/> 
    1616    <rect x="19" y="32" fill="#B2B2B2" width="10" height="8"/> 
    1717    <rect x="19" y="23" fill="#B2B2B2" width="10" height="8"/> 
    18     <rect x="19" y="14" fill="#B2B2B2" width="10" height="0"/> 
     18    <polygon fill="#B2B2B2" points="19,14 29,14 29,14   "/> 
    1919    <rect x="19" y="14" fill="#B2B2B2" width="10" height="8"/> 
    2020    <rect x="30" y="23" fill="#FFFFFF" width="10" height="8"/> 
    21     <rect x="30" y="14" fill="#FFFFFF" width="10" height="0"/> 
     21    <polygon fill="#FFFFFF" points="30,14 40,14 40,14   "/> 
    2222    <rect x="30" y="14" fill="#FFFFFF" width="10" height="8"/> 
    2323    <rect x="19" y="22" fill="#989898" width="10" height="1"/> 
     
    2525</g> 
    2626<g> 
    27     <path fill="#333333" d="M41.782,38.076c-0.075-1.428-0.216-2.852-0.373-4.271c-0.065-0.586-0.14-1.171-0.212-1.758 
    28         c-0.049-0.406-0.056-0.83-0.236-1.195c-0.038-0.08-0.085-0.158-0.142-0.23c-0.297-0.379-0.743-0.65-1.219-0.726 
    29         c-0.065-0.011-0.129-0.017-0.192-0.017c-0.207,0-0.398,0.061-0.571,0.195c-0.271,0.211-0.374,0.604-0.626,0.803 
    30         c-0.077,0.061-0.154,0.086-0.23,0.086c-0.344,0-0.678-0.515-0.937-0.666c-0.237-0.14-0.506-0.21-0.776-0.21 
    31         c-0.192,0-0.384,0.036-0.566,0.107c-0.248,0.099-0.503,0.292-0.667,0.532c-0.114,0.167-0.171,0.596-0.41,0.596 
    32         c-0.021,0-0.042-0.003-0.066-0.01c-0.208-0.06-0.439-0.7-0.604-0.849C33.712,30.245,33.406,30,33.084,30c-0.009,0-0.019,0-0.028,0 
    33         c-0.635,0-1.221,0.485-1.454,1.101c-0.169,0.444-0.064,0.882,0.06,1.312c0.001,0.006,0.003,0.05,0.004,0.055 
    34         c-0.001-0.005-0.003,0.011-0.004,0.005c-0.226-0.778-0.563-1.757-0.857-2.511c-0.325-0.832-1.587-4.519-2.455-4.872 
    35         c-0.157-0.064-0.323-0.091-0.491-0.091c-0.195,0-0.392,0.042-0.576,0.116c-0.33,0.131-0.604,0.374-0.774,0.685 
    36         c-0.552,1.006,0.201,2.552,0.559,3.474c0.824,2.125,1.051,2.746,1.729,4.921c0.229,0.735,0.948,3.345,0.988,4.087 
    37         c-0.781-0.337-1.928-1.167-2.894-1.167c-0.178,0-0.351,0.028-0.513,0.093c-0.451,0.18-0.794,0.568-0.916,1.039 
    38         c-0.059,0.23-0.079,0.496-0.017,0.73c0.343,1.283,1.803,1.906,2.866,2.484c0.938,0.51,1.879,1.002,2.778,1.582 
    39         c0.608,0.395,1.205,0.807,1.826,1.18c0.388,0.235,1.091,0.778,1.589,0.778c0.022,0,0.044-0.001,0.066-0.003 
    40         c0.333-0.033,0.769-0.346,1.086-0.471c0.438-0.174,0.874-0.348,1.311-0.521c1.105-0.439,2.208-0.887,3.315-1.32 
    41         c0.549-0.213,1.416-0.316,1.634-0.959c0.225-0.664-0.078-1.533-0.048-2.227C41.889,39.025,41.808,38.545,41.782,38.076z"/> 
    42     <path fill="#FFFFFF" d="M42.924,40.223c-0.033-0.258-0.064-0.501-0.056-0.681c0.016-0.376-0.019-0.74-0.049-1.062 
    43         c-0.015-0.153-0.029-0.307-0.038-0.458c-0.08-1.525-0.235-3.044-0.377-4.327c-0.049-0.438-0.103-0.874-0.157-1.312l-0.057-0.458 
    44         l-0.023-0.222c-0.038-0.374-0.084-0.839-0.308-1.294c-0.062-0.133-0.15-0.275-0.252-0.406c-0.457-0.583-1.131-0.982-1.85-1.096 
    45         c-0.115-0.02-0.232-0.029-0.348-0.029c-0.436,0-0.846,0.141-1.187,0.407c-0.151,0.118-0.269,0.251-0.364,0.378 
    46         c-0.092-0.082-0.193-0.162-0.308-0.229c-0.385-0.228-0.829-0.348-1.282-0.348c-0.321,0-0.636,0.06-0.934,0.178 
    47         s-0.579,0.303-0.812,0.529c-0.388-0.308-0.88-0.473-1.431-0.476h0c-0.491,0-0.947,0.12-1.335,0.336 
    48         c-0.006-0.017-0.012-0.032-0.018-0.048c-0.041-0.104-0.096-0.253-0.164-0.436c-1.258-3.387-1.976-4.649-2.845-5.003 
    49         C28.456,24.057,28.163,24,27.859,24c-0.321,0-0.64,0.062-0.948,0.186c-0.54,0.214-0.995,0.616-1.278,1.13 
    50         c-0.717,1.307-0.063,2.919,0.37,3.985c0.048,0.117,0.093,0.229,0.132,0.331c0.819,2.112,1.038,2.713,1.707,4.857 
    51         c0.143,0.46,0.357,1.23,0.545,1.97c-0.477-0.2-0.985-0.348-1.496-0.348c-0.313,0-0.61,0.055-0.883,0.164 
    52         c-0.747,0.298-1.312,0.939-1.514,1.718c-0.077,0.304-0.142,0.762-0.015,1.238c0.416,1.558,1.899,2.338,2.982,2.907 
    53         c0.128,0.067,0.252,0.133,0.372,0.198l0.422,0.229c0.802,0.433,1.56,0.843,2.292,1.314c0.188,0.122,0.376,0.247,0.564,0.371 
    54         c0.425,0.282,0.851,0.563,1.288,0.826c0.064,0.039,0.143,0.091,0.229,0.147c0.497,0.327,1.176,0.773,1.875,0.773 
    55         c0.058,0,0.115-0.003,0.171-0.009c0.406-0.04,0.779-0.239,1.078-0.399c0.097-0.051,0.188-0.103,0.27-0.135l1.314-0.523l1.215-0.485 
    56         c0.698-0.279,1.396-0.559,2.095-0.833c0.096-0.036,0.208-0.069,0.323-0.105c0.583-0.178,1.558-0.476,1.893-1.463 
    57         C43.076,41.417,42.995,40.782,42.924,40.223z M41.917,41.725c-0.218,0.643-1.085,0.746-1.634,0.959 
    58         c-1.107,0.434-2.21,0.881-3.315,1.32c-0.437,0.174-0.873,0.348-1.311,0.521c-0.317,0.125-0.753,0.438-1.086,0.471 
    59         c-0.021,0.002-0.043,0.003-0.066,0.003c-0.498,0-1.201-0.543-1.589-0.778c-0.621-0.373-1.217-0.785-1.826-1.18 
    60         c-0.899-0.58-1.84-1.072-2.778-1.582c-1.063-0.578-2.523-1.201-2.866-2.484c-0.062-0.234-0.042-0.5,0.017-0.73 
    61         c0.122-0.471,0.465-0.859,0.916-1.039c0.163-0.064,0.335-0.093,0.513-0.093c0.966,0,2.113,0.83,2.894,1.167 
    62         c-0.041-0.742-0.76-3.352-0.988-4.087c-0.678-2.175-0.905-2.796-1.729-4.921c-0.357-0.922-1.111-2.467-0.559-3.473 
    63         c0.171-0.311,0.444-0.554,0.774-0.685C27.468,25.04,27.664,25,27.859,25c0.167,0,0.334,0.029,0.491,0.094 
    64         c0.868,0.354,2.13,4.045,2.455,4.877c0.294,0.754,0.631,1.742,0.857,2.521c-0.125-0.43-0.229-0.946-0.06-1.391 
    65         C31.835,30.485,32.421,30,33.056,30c0.01,0,0.019,0,0.028,0c0.321,0,0.627,0.245,0.87,0.464c0.166,0.148,0.396,0.71,0.604,0.769 
    66         c0.023,0.008,0.045,0.05,0.066,0.05c0.238,0,0.296-0.408,0.41-0.575c0.164-0.24,0.419-0.424,0.667-0.522 
    67         c0.182-0.071,0.374-0.103,0.566-0.103c0.271,0,0.54,0.072,0.776,0.212c0.259,0.151,0.593,0.668,0.937,0.668 
    68         c0.077,0,0.154-0.025,0.23-0.086c0.252-0.198,0.355-0.591,0.626-0.802c0.173-0.135,0.365-0.195,0.571-0.195 
    69         c0.062,0,0.127,0.006,0.192,0.017c0.476,0.075,0.922,0.347,1.219,0.726c0.057,0.072,0.104,0.15,0.142,0.23 
    70         c0.18,0.365,0.187,0.789,0.236,1.195c0.072,0.587,0.147,1.172,0.212,1.758c0.157,1.42,0.298,2.844,0.373,4.271 
    71         c0.025,0.469,0.106,0.949,0.086,1.422C41.838,40.191,42.141,41.061,41.917,41.725z"/> 
     27    <path fill="#333333" d="M32.179,41.76c0.44-1.36,0.817-2.74,1.178-4.123c0.148-0.57,0.288-1.144,0.43-1.718 
     28        c0.1-0.396,0.244-0.795,0.206-1.2c-0.007-0.089-0.022-0.179-0.051-0.266c-0.142-0.46-0.461-0.872-0.879-1.113 
     29        c-0.057-0.034-0.114-0.062-0.173-0.085c-0.193-0.073-0.394-0.085-0.604-0.021c-0.328,0.101-0.564,0.431-0.871,0.526 
     30        c-0.094,0.029-0.175,0.025-0.246-0.002c-0.321-0.123-0.449-0.724-0.637-0.957c-0.172-0.216-0.397-0.376-0.65-0.473 
     31        c-0.179-0.068-0.371-0.104-0.566-0.103c-0.267,0.004-0.574,0.094-0.813,0.26c-0.166,0.114-0.373,0.494-0.596,0.409 
     32        c-0.02-0.007-0.037-0.019-0.059-0.032c-0.172-0.131-0.159-0.812-0.26-1.01c-0.149-0.29-0.348-0.628-0.648-0.743 
     33        c-0.008-0.003-0.019-0.007-0.025-0.01c-0.594-0.228-1.314,0.016-1.753,0.509c-0.315,0.354-0.374,0.801-0.412,1.247 
     34        c-0.001,0.006-0.015,0.048-0.016,0.054c0.001-0.006-0.007,0.008-0.005,0.002c0.065-0.807,0.102-1.842,0.096-2.651 
     35        c-0.006-0.894,0.132-4.787-0.553-5.428c-0.123-0.116-0.269-0.199-0.426-0.26c-0.183-0.07-0.382-0.101-0.58-0.098 
     36        c-0.354,0.004-0.696,0.133-0.968,0.363c-0.875,0.743-0.724,2.455-0.718,3.444c0.011,2.279,0.001,2.94-0.144,5.215 
     37        c-0.049,0.768-0.309,3.462-0.536,4.169c-0.61-0.593-1.385-1.777-2.287-2.123c-0.166-0.063-0.338-0.1-0.512-0.097 
     38        c-0.486,0.008-0.944,0.247-1.228,0.644c-0.138,0.193-0.251,0.436-0.275,0.676c-0.139,1.32,1.003,2.424,1.788,3.344 
     39        c0.695,0.812,1.397,1.606,2.03,2.47c0.428,0.586,0.838,1.184,1.284,1.754c0.278,0.358,0.741,1.117,1.206,1.295 
     40        c0.021,0.008,0.042,0.015,0.063,0.021c0.322,0.087,0.841-0.049,1.183-0.053c0.471-0.006,0.939-0.013,1.41-0.019 
     41        c1.189-0.016,2.379-0.039,3.568-0.049c0.589-0.003,1.435,0.211,1.868-0.312c0.447-0.54,0.475-1.46,0.751-2.097 
     42        C31.939,42.685,32.036,42.207,32.179,41.76z"/> 
     43    <path fill="#FFFFFF" d="M32.479,44.173c0.062-0.253,0.118-0.49,0.191-0.656c0.148-0.346,0.247-0.697,0.333-1.009 
     44        c0.042-0.148,0.083-0.298,0.129-0.442c0.47-1.452,0.867-2.926,1.193-4.176c0.11-0.427,0.216-0.854,0.321-1.281l0.11-0.449 
     45        l0.058-0.215c0.099-0.363,0.222-0.814,0.176-1.318c-0.011-0.147-0.043-0.311-0.091-0.47c-0.219-0.707-0.706-1.32-1.337-1.685 
     46        c-0.1-0.06-0.206-0.109-0.314-0.151c-0.407-0.155-0.842-0.17-1.254-0.043c-0.184,0.055-0.341,0.138-0.476,0.223 
     47        c-0.057-0.109-0.122-0.221-0.206-0.324c-0.277-0.351-0.649-0.621-1.073-0.783c-0.299-0.114-0.615-0.17-0.936-0.167 
     48        c-0.32,0.004-0.648,0.075-0.947,0.204c-0.252-0.427-0.652-0.756-1.167-0.956l0,0c-0.458-0.176-0.927-0.226-1.366-0.163 
     49        c0-0.017,0-0.033,0-0.051c-0.001-0.112,0.001-0.271,0.003-0.466c0.035-3.613-0.186-5.049-0.871-5.689 
     50        c-0.216-0.199-0.469-0.357-0.753-0.465c-0.3-0.115-0.62-0.171-0.952-0.165c-0.58,0.007-1.149,0.221-1.598,0.599 
     51        c-1.136,0.964-1.101,2.703-1.077,3.854c0.003,0.127,0.005,0.247,0.005,0.356c0.011,2.266,0,2.905-0.141,5.146 
     52        c-0.031,0.481-0.105,1.277-0.194,2.036c-0.374-0.357-0.796-0.677-1.273-0.859c-0.292-0.111-0.589-0.167-0.883-0.162 
     53        c-0.804,0.011-1.562,0.407-2.028,1.063c-0.181,0.256-0.405,0.661-0.456,1.15c-0.168,1.604,0.939,2.862,1.747,3.781 
     54        c0.096,0.108,0.188,0.215,0.277,0.317l0.312,0.364c0.594,0.691,1.155,1.345,1.671,2.046c0.132,0.182,0.263,0.365,0.395,0.549 
     55        c0.296,0.414,0.594,0.829,0.907,1.231c0.046,0.06,0.102,0.136,0.162,0.219c0.348,0.483,0.822,1.143,1.475,1.392 
     56        c0.054,0.021,0.109,0.039,0.163,0.053c0.394,0.108,0.813,0.056,1.149,0.013c0.109-0.013,0.212-0.03,0.3-0.029l1.415-0.02 
     57        l1.308-0.019c0.752-0.012,1.504-0.023,2.256-0.03c0.102,0.001,0.218,0.01,0.338,0.018c0.609,0.042,1.626,0.112,2.29-0.69 
     58        C32.194,45.343,32.346,44.72,32.479,44.173z M31.002,45.216c-0.434,0.523-1.28,0.309-1.869,0.312 
     59        c-1.189,0.011-2.378,0.034-3.568,0.05c-0.47,0.006-0.938,0.013-1.41,0.019c-0.341,0.003-0.859,0.14-1.183,0.052 
     60        c-0.021-0.006-0.042-0.013-0.062-0.021c-0.466-0.178-0.927-0.936-1.206-1.294c-0.447-0.57-0.856-1.168-1.284-1.754 
     61        c-0.632-0.863-1.336-1.659-2.029-2.47c-0.787-0.92-1.928-2.023-1.791-3.345c0.026-0.241,0.141-0.481,0.277-0.675 
     62        c0.282-0.397,0.741-0.637,1.227-0.644c0.175-0.001,0.347,0.032,0.513,0.097c0.901,0.345,1.677,1.529,2.286,2.123 
     63        c0.227-0.707,0.487-3.402,0.537-4.17c0.144-2.274,0.153-2.936,0.143-5.215c-0.004-0.988-0.156-2.701,0.719-3.443 
     64        c0.271-0.229,0.612-0.358,0.968-0.363c0.197-0.003,0.396,0.03,0.578,0.1c0.154,0.06,0.301,0.146,0.424,0.263 
     65        c0.685,0.641,0.546,4.539,0.552,5.432c0.006,0.81-0.033,1.853-0.101,2.662c0.037-0.446,0.125-0.966,0.44-1.321 
     66        c0.438-0.493,1.159-0.736,1.753-0.509c0.009,0.004,0.018,0.007,0.025,0.01c0.299,0.114,0.498,0.453,0.646,0.744 
     67        c0.103,0.197,0.116,0.805,0.291,0.934c0.018,0.017,0.023,0.063,0.043,0.071c0.222,0.084,0.422-0.276,0.588-0.392 
     68        c0.239-0.164,0.543-0.246,0.81-0.249c0.195-0.001,0.387,0.037,0.566,0.106c0.252,0.097,0.478,0.259,0.648,0.475 
     69        c0.188,0.233,0.315,0.836,0.637,0.959c0.071,0.027,0.152,0.031,0.245,0.002c0.306-0.095,0.542-0.426,0.871-0.525 
     70        c0.21-0.065,0.411-0.052,0.604,0.021c0.058,0.021,0.116,0.052,0.174,0.084c0.417,0.241,0.736,0.654,0.879,1.114 
     71        c0.026,0.088,0.043,0.178,0.05,0.266c0.038,0.406-0.108,0.804-0.207,1.199c-0.143,0.574-0.281,1.148-0.43,1.72 
     72        c-0.361,1.382-0.737,2.762-1.177,4.122c-0.144,0.446-0.24,0.923-0.428,1.358C31.476,43.755,31.448,44.675,31.002,45.216z"/> 
    7273</g> 
    7374</svg> 
  • Orange/OrangeWidgets/Data/icons/SelectData.svg

    r11217 r11638  
    11<?xml version="1.0" encoding="utf-8"?> 
    2 <!-- Generator: Adobe Illustrator 16.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> 
     2<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> 
    33<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 
    44<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" 
    55     width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve"> 
    66<g> 
    7     <path fill="#333333" d="M6,6v36h36V6H6z M40,14v8v1v8v1v8H30h-1H19h-1H8v-8v-1v-8v-1v-8v0h10l0,0h1l0,0h10l0,0h1l0,0L40,14L40,14z" 
     7    <path fill="#333333" d="M6,6v36h36V6H6z M40,14v8v1v8v1v8H30h-1H19h-1H8v-8v-1v-8v-1v-8l0,0h10l0,0h1l0,0h10l0,0h1l0,0H40L40,14z" 
    88        /> 
    99    <polygon fill="#666666" points="30,14 29,14 29,22 19,22 19,14 18,14 18,22 8,22 8,23 18,23 18,23 19,23 19,23 29,23 29,23 30,23  
     
    1313    <rect x="8" y="32" fill="#FFFFFF" width="10" height="8"/> 
    1414    <rect x="8" y="23" fill="#B2B2B2" width="10" height="8"/> 
    15     <rect x="8" y="14" fill="#FFFFFF" width="10" height="0"/> 
     15    <polygon fill="#FFFFFF" points="8,14 18,14 18,14    "/> 
    1616    <rect x="8" y="14" fill="#FFFFFF" width="10" height="8"/> 
    1717    <rect x="19" y="32" fill="#FFFFFF" width="10" height="8"/> 
    1818    <rect x="19" y="23" fill="#B2B2B2" width="10" height="8"/> 
    19     <rect x="19" y="14" fill="#FFFFFF" width="10" height="0"/> 
     19    <polygon fill="#FFFFFF" points="19,14 29,14 29,14   "/> 
    2020    <rect x="19" y="14" fill="#FFFFFF" width="10" height="8"/> 
    2121    <rect x="30" y="32" fill="#FFFFFF" width="10" height="8"/> 
    2222    <rect x="30" y="23" fill="#B2B2B2" width="10" height="8"/> 
    23     <rect x="30" y="14" fill="#FFFFFF" width="10" height="0"/> 
     23    <polygon fill="#FFFFFF" points="30,14 40,14 40,14   "/> 
    2424    <rect x="30" y="14" fill="#FFFFFF" width="10" height="8"/> 
    2525    <polygon fill="#989898" points="19,31 19,23 19,23 18,23 18,23 18,31     "/> 
     
    2727</g> 
    2828<g> 
    29     <path fill="#333333" d="M41.782,38.076c-0.075-1.428-0.216-2.852-0.373-4.271c-0.065-0.586-0.14-1.171-0.212-1.758 
    30         c-0.049-0.406-0.056-0.83-0.236-1.195c-0.038-0.08-0.085-0.158-0.142-0.23c-0.297-0.379-0.743-0.65-1.219-0.726 
    31         c-0.065-0.011-0.129-0.017-0.192-0.017c-0.207,0-0.398,0.061-0.571,0.195c-0.271,0.211-0.374,0.604-0.626,0.803 
    32         c-0.077,0.061-0.154,0.086-0.23,0.086c-0.344,0-0.678-0.515-0.937-0.666c-0.237-0.14-0.506-0.21-0.776-0.21 
    33         c-0.192,0-0.384,0.036-0.566,0.107c-0.248,0.099-0.503,0.292-0.667,0.532c-0.114,0.167-0.171,0.596-0.41,0.596 
    34         c-0.021,0-0.042-0.003-0.066-0.01c-0.208-0.06-0.439-0.7-0.604-0.849C33.712,30.245,33.406,30,33.084,30c-0.009,0-0.019,0-0.028,0 
    35         c-0.635,0-1.221,0.485-1.454,1.101c-0.169,0.444-0.064,0.882,0.06,1.312c0.001,0.006,0.003,0.05,0.004,0.055 
    36         c-0.001-0.005-0.003,0.011-0.004,0.005c-0.226-0.778-0.563-1.757-0.857-2.511c-0.325-0.832-1.587-4.519-2.455-4.872 
    37         c-0.157-0.064-0.323-0.091-0.491-0.091c-0.195,0-0.392,0.042-0.576,0.116c-0.33,0.131-0.604,0.374-0.774,0.685 
    38         c-0.552,1.006,0.201,2.552,0.559,3.474c0.824,2.125,1.051,2.746,1.729,4.921c0.229,0.735,0.948,3.345,0.988,4.087 
    39         c-0.781-0.337-1.928-1.167-2.894-1.167c-0.178,0-0.351,0.028-0.513,0.093c-0.451,0.18-0.794,0.568-0.916,1.039 
    40         c-0.059,0.23-0.079,0.496-0.017,0.73c0.343,1.283,1.803,1.906,2.866,2.484c0.938,0.51,1.879,1.002,2.778,1.582 
    41         c0.608,0.395,1.205,0.807,1.826,1.18c0.388,0.235,1.091,0.778,1.589,0.778c0.022,0,0.044-0.001,0.066-0.003 
    42         c0.333-0.033,0.769-0.346,1.086-0.471c0.438-0.174,0.874-0.348,1.311-0.521c1.105-0.439,2.208-0.887,3.315-1.32 
    43         c0.549-0.213,1.416-0.316,1.634-0.959c0.225-0.664-0.078-1.533-0.048-2.227C41.889,39.025,41.808,38.545,41.782,38.076z"/> 
    44     <path fill="#FFFFFF" d="M42.924,40.223c-0.033-0.258-0.064-0.501-0.056-0.681c0.016-0.376-0.019-0.74-0.049-1.062 
    45         c-0.015-0.153-0.029-0.307-0.038-0.458c-0.08-1.525-0.235-3.044-0.377-4.327c-0.049-0.438-0.103-0.874-0.157-1.312l-0.057-0.458 
    46         l-0.023-0.222c-0.038-0.374-0.084-0.839-0.308-1.294c-0.062-0.133-0.15-0.275-0.252-0.406c-0.457-0.583-1.131-0.982-1.85-1.096 
    47         c-0.115-0.02-0.232-0.029-0.348-0.029c-0.436,0-0.846,0.141-1.187,0.407c-0.151,0.118-0.269,0.251-0.364,0.378 
    48         c-0.092-0.082-0.193-0.162-0.308-0.229c-0.385-0.228-0.829-0.348-1.282-0.348c-0.321,0-0.636,0.06-0.934,0.178 
    49         s-0.579,0.303-0.812,0.529c-0.388-0.308-0.88-0.473-1.431-0.476h0c-0.491,0-0.947,0.12-1.335,0.336 
    50         c-0.006-0.017-0.012-0.032-0.018-0.048c-0.041-0.104-0.096-0.253-0.164-0.436c-1.258-3.387-1.976-4.649-2.845-5.003 
    51         C28.456,24.057,28.163,24,27.859,24c-0.321,0-0.64,0.062-0.948,0.186c-0.54,0.214-0.995,0.616-1.278,1.13 
    52         c-0.717,1.307-0.063,2.919,0.37,3.985c0.048,0.117,0.093,0.229,0.132,0.331c0.819,2.112,1.038,2.713,1.707,4.857 
    53         c0.143,0.46,0.357,1.23,0.545,1.97c-0.477-0.2-0.985-0.348-1.496-0.348c-0.313,0-0.61,0.055-0.883,0.164 
    54         c-0.747,0.298-1.312,0.939-1.514,1.718c-0.077,0.304-0.142,0.762-0.015,1.238c0.416,1.558,1.899,2.338,2.982,2.907 
    55         c0.128,0.067,0.252,0.133,0.372,0.198l0.422,0.229c0.802,0.433,1.56,0.843,2.292,1.314c0.188,0.122,0.376,0.247,0.564,0.371 
    56         c0.425,0.282,0.851,0.563,1.288,0.826c0.064,0.039,0.143,0.091,0.229,0.147c0.497,0.327,1.176,0.773,1.875,0.773 
    57         c0.058,0,0.115-0.003,0.171-0.009c0.406-0.04,0.779-0.239,1.078-0.399c0.097-0.051,0.188-0.103,0.27-0.135l1.314-0.523l1.215-0.485 
    58         c0.698-0.279,1.396-0.559,2.095-0.833c0.096-0.036,0.208-0.069,0.323-0.105c0.583-0.178,1.558-0.476,1.893-1.463 
    59         C43.076,41.417,42.995,40.782,42.924,40.223z M41.917,41.725c-0.218,0.643-1.085,0.746-1.634,0.959 
    60         c-1.107,0.434-2.21,0.881-3.315,1.32c-0.437,0.174-0.873,0.348-1.311,0.521c-0.317,0.125-0.753,0.438-1.086,0.471 
    61         c-0.021,0.002-0.043,0.003-0.066,0.003c-0.498,0-1.201-0.543-1.589-0.778c-0.621-0.373-1.217-0.785-1.826-1.18 
    62         c-0.899-0.58-1.84-1.072-2.778-1.582c-1.063-0.578-2.523-1.201-2.866-2.484c-0.062-0.234-0.042-0.5,0.017-0.73 
    63         c0.122-0.471,0.465-0.859,0.916-1.039c0.163-0.064,0.335-0.093,0.513-0.093c0.966,0,2.113,0.83,2.894,1.167 
    64         c-0.041-0.742-0.76-3.352-0.988-4.087c-0.678-2.175-0.905-2.796-1.729-4.921c-0.357-0.922-1.111-2.467-0.559-3.473 
    65         c0.171-0.311,0.444-0.554,0.774-0.685C27.468,25.04,27.664,25,27.859,25c0.167,0,0.334,0.029,0.491,0.094 
    66         c0.868,0.354,2.13,4.045,2.455,4.877c0.294,0.754,0.631,1.742,0.857,2.521c-0.125-0.43-0.229-0.946-0.06-1.391 
    67         C31.835,30.485,32.421,30,33.056,30c0.01,0,0.019,0,0.028,0c0.321,0,0.627,0.245,0.87,0.464c0.166,0.148,0.396,0.71,0.604,0.769 
    68         c0.023,0.008,0.045,0.05,0.066,0.05c0.238,0,0.296-0.408,0.41-0.575c0.164-0.24,0.419-0.424,0.667-0.522 
    69         c0.182-0.071,0.374-0.103,0.566-0.103c0.271,0,0.54,0.072,0.776,0.212c0.259,0.151,0.593,0.668,0.937,0.668 
    70         c0.077,0,0.154-0.025,0.23-0.086c0.252-0.198,0.355-0.591,0.626-0.802c0.173-0.135,0.365-0.195,0.571-0.195 
    71         c0.062,0,0.127,0.006,0.192,0.017c0.476,0.075,0.922,0.347,1.219,0.726c0.057,0.072,0.104,0.15,0.142,0.23 
    72         c0.18,0.365,0.187,0.789,0.236,1.195c0.072,0.587,0.147,1.172,0.212,1.758c0.157,1.42,0.298,2.844,0.373,4.271 
    73         c0.025,0.469,0.106,0.949,0.086,1.422C41.838,40.191,42.141,41.061,41.917,41.725z"/> 
     29    <path fill="#333333" d="M43.684,29.709c-0.851-1.15-1.752-2.261-2.665-3.359c-0.378-0.452-0.763-0.898-1.146-1.349 
     30        c-0.265-0.312-0.506-0.661-0.856-0.867c-0.076-0.045-0.158-0.086-0.246-0.112c-0.457-0.153-0.978-0.133-1.417,0.066 
     31        c-0.06,0.025-0.117,0.056-0.17,0.09c-0.173,0.114-0.298,0.271-0.368,0.479c-0.109,0.325,0.021,0.71-0.08,1.016 
     32        c-0.031,0.091-0.081,0.157-0.145,0.197c-0.287,0.19-0.851-0.057-1.148-0.039c-0.275,0.013-0.538,0.104-0.764,0.253 
     33        c-0.16,0.105-0.301,0.241-0.413,0.4c-0.152,0.22-0.258,0.521-0.263,0.812c-0.004,0.202,0.186,0.592-0.014,0.724 
     34        c-0.018,0.012-0.038,0.019-0.061,0.028c-0.207,0.063-0.753-0.344-0.972-0.377c-0.323-0.047-0.714-0.083-0.982,0.095 
     35        c-0.007,0.005-0.017,0.011-0.021,0.015c-0.531,0.351-0.755,1.078-0.608,1.722c0.104,0.462,0.433,0.77,0.773,1.06 
     36        c0.004,0.006,0.029,0.041,0.034,0.047c-0.005-0.006,0.002,0.009-0.002,0.004c-0.617-0.524-1.438-1.155-2.099-1.623 
     37        c-0.729-0.515-3.815-2.895-4.733-2.711c-0.166,0.032-0.319,0.103-0.459,0.194c-0.164,0.108-0.306,0.252-0.417,0.414 
     38        c-0.203,0.292-0.297,0.645-0.27,0.999c0.095,1.145,1.575,2.018,2.382,2.59c1.86,1.318,2.391,1.711,4.156,3.153 
     39        c0.595,0.487,2.634,2.269,3.076,2.865c-0.837,0.149-2.252,0.089-3.059,0.622c-0.146,0.098-0.276,0.216-0.375,0.36 
     40        c-0.278,0.399-0.35,0.91-0.192,1.371c0.076,0.225,0.208,0.457,0.389,0.618c0.993,0.882,2.555,0.597,3.76,0.493 
     41        c1.064-0.093,2.12-0.199,3.19-0.211c0.726-0.007,1.45,0.009,2.174-0.022c0.453-0.018,1.339,0.048,1.755-0.228 
     42        c0.018-0.012,0.036-0.025,0.055-0.039c0.258-0.21,0.449-0.712,0.646-0.991c0.269-0.386,0.536-0.771,0.806-1.157 
     43        c0.68-0.977,1.353-1.958,2.038-2.93c0.34-0.479,1.007-1.043,0.835-1.7c-0.18-0.678-0.912-1.235-1.268-1.831 
     44        C44.296,30.44,43.964,30.085,43.684,29.709z"/> 
     45    <path fill="#FFFFFF" d="M45.819,30.87c-0.17-0.196-0.329-0.382-0.422-0.537c-0.193-0.321-0.423-0.607-0.625-0.858 
     46        c-0.098-0.12-0.194-0.241-0.285-0.363c-0.907-1.227-1.873-2.408-2.699-3.401c-0.282-0.339-0.569-0.673-0.854-1.007l-0.301-0.351 
     47        l-0.142-0.174c-0.237-0.29-0.533-0.654-0.97-0.909c-0.126-0.077-0.276-0.147-0.434-0.2c-0.703-0.234-1.484-0.196-2.148,0.104 
     48        c-0.106,0.048-0.21,0.104-0.307,0.168c-0.363,0.24-0.628,0.584-0.765,0.995c-0.063,0.181-0.087,0.357-0.097,0.517 
     49        c-0.121-0.018-0.249-0.029-0.384-0.021c-0.447,0.021-0.884,0.166-1.261,0.415c-0.268,0.177-0.497,0.4-0.681,0.664 
     50        c-0.185,0.263-0.316,0.571-0.387,0.89c-0.493-0.044-0.993,0.09-1.457,0.391l0,0c-0.409,0.271-0.721,0.622-0.927,1.017 
     51        c-0.014-0.011-0.027-0.02-0.042-0.029c-0.091-0.065-0.22-0.16-0.378-0.274c-2.916-2.133-4.211-2.79-5.131-2.605 
     52        c-0.288,0.059-0.564,0.173-0.817,0.341c-0.268,0.178-0.5,0.405-0.688,0.678c-0.332,0.477-0.49,1.064-0.444,1.648 
     53        c0.123,1.484,1.557,2.468,2.506,3.118c0.104,0.073,0.203,0.142,0.292,0.205c1.848,1.311,2.361,1.692,4.102,3.111 
     54        c0.373,0.306,0.977,0.829,1.542,1.343c-0.509,0.096-1.015,0.253-1.441,0.535c-0.26,0.172-0.478,0.382-0.645,0.623 
     55        c-0.46,0.659-0.579,1.507-0.316,2.269c0.102,0.295,0.301,0.714,0.669,1.041c1.206,1.071,2.873,0.902,4.091,0.782 
     56        c0.144-0.016,0.283-0.028,0.42-0.041l0.478-0.041c0.907-0.082,1.766-0.157,2.637-0.167c0.224-0.002,0.45-0.002,0.676-0.003 
     57        c0.51,0.001,1.019,0.001,1.528-0.019c0.076-0.004,0.17-0.004,0.272-0.005c0.595-0.001,1.408-0.004,1.991-0.389 
     58        c0.048-0.032,0.095-0.066,0.137-0.103c0.317-0.256,0.52-0.628,0.68-0.927c0.054-0.097,0.101-0.189,0.151-0.26l0.808-1.162 
     59        l0.746-1.074c0.43-0.617,0.857-1.235,1.29-1.851c0.06-0.083,0.136-0.172,0.21-0.265c0.391-0.471,1.038-1.257,0.773-2.265 
     60        C46.604,31.783,46.186,31.297,45.819,30.87z M45.807,32.679c0.174,0.657-0.493,1.221-0.834,1.701 
     61        c-0.684,0.973-1.357,1.952-2.037,2.928c-0.27,0.387-0.536,0.771-0.807,1.158c-0.196,0.28-0.387,0.78-0.647,0.992 
     62        c-0.016,0.012-0.033,0.026-0.053,0.037c-0.416,0.275-1.302,0.21-1.755,0.228c-0.725,0.031-1.448,0.017-2.175,0.023 
     63        c-1.068,0.011-2.126,0.119-3.188,0.211c-1.206,0.104-2.768,0.389-3.763-0.493c-0.179-0.161-0.309-0.394-0.387-0.618 
     64        c-0.158-0.461-0.085-0.972,0.191-1.372c0.102-0.143,0.229-0.261,0.378-0.36c0.805-0.532,2.221-0.472,3.057-0.622 
     65        c-0.442-0.596-2.481-2.377-3.077-2.864c-1.765-1.442-2.298-1.835-4.156-3.153c-0.805-0.573-2.286-1.445-2.381-2.589 
     66        c-0.026-0.354,0.066-0.708,0.269-1c0.113-0.161,0.256-0.303,0.418-0.41c0.139-0.092,0.295-0.159,0.461-0.193 
     67        c0.919-0.184,4.007,2.2,4.737,2.716c0.661,0.467,1.486,1.106,2.104,1.632c-0.341-0.29-0.712-0.663-0.816-1.127 
     68        c-0.146-0.644,0.077-1.371,0.608-1.722c0.007-0.006,0.015-0.01,0.021-0.015c0.267-0.178,0.657-0.142,0.981-0.094 
     69        c0.22,0.032,0.722,0.376,0.929,0.309c0.022-0.005,0.065,0.019,0.082,0.007c0.198-0.13,0.021-0.504,0.025-0.706 
     70        c0.005-0.291,0.115-0.585,0.269-0.804c0.112-0.159,0.255-0.292,0.415-0.398c0.226-0.148,0.49-0.237,0.765-0.249 
     71        c0.298-0.018,0.863,0.229,1.15,0.039c0.063-0.04,0.113-0.106,0.144-0.197c0.102-0.304-0.029-0.689,0.082-1.015 
     72        c0.067-0.208,0.196-0.364,0.367-0.479c0.053-0.033,0.11-0.064,0.17-0.091c0.438-0.199,0.96-0.219,1.418-0.066 
     73        c0.086,0.028,0.169,0.068,0.245,0.114c0.351,0.206,0.59,0.556,0.855,0.866c0.383,0.45,0.769,0.897,1.146,1.351 
     74        c0.914,1.1,1.818,2.208,2.666,3.358c0.279,0.376,0.611,0.732,0.855,1.138C44.896,31.442,45.628,32.001,45.807,32.679z"/> 
    7475</g> 
    7576</svg> 
  • Orange/OrangeWidgets/Evaluate/OWPredictions.py

    r11217 r11660  
    129129 
    130130class OWPredictions(OWWidget): 
    131     settingsList = ["showProb", "showClass", "ShowAttributeMethod", "sendOnChange", "precision"] 
     131    contextHandlers = { 
     132        "": ClassValuesContextHandler("", ["selectedClasses"]) 
     133    } 
     134    settingsList = ["showProb", "showClass", "ShowAttributeMethod", 
     135                    "sendOnChange", "precision"] 
    132136 
    133137    def __init__(self, parent=None, signalManager = None): 
     
    383387        if self.data: 
    384388            self.setDataModel(self.data) 
     389            self.openContext("", list(self.classes)) 
    385390            self.setPredictionModel(self.predictors.values(), self.data) 
     391 
    386392        self.checksendpredictions() 
    387393 
    388394    def setData(self, data): 
    389         self.handledAllSignalsFlag = False 
    390         if not data: 
     395        """ 
     396        Set input data table. 
     397        """ 
     398        self.closeContext("") 
     399        if data is None: 
    391400            self.data = data 
    392401            self.datalabel = "N/A" 
  • Orange/OrangeWidgets/OWBaseWidget.py

    r11548 r11617  
    44# A General Orange Widget, from which all the Orange Widgets are derived 
    55# 
     6import warnings 
     7import shutil 
     8 
    69from Orange.utils import environ 
    710from Orange.orng.orngEnviron import directoryNames as old_directory_names 
     
    204207        self.__wasShown = False 
    205208        self.__progressBarValue = -1 
     209        self.__progressState = 0 
    206210 
    207211    # uncomment this when you need to see which events occured 
     
    449453 
    450454    def getDefaultSettingsFilename(self): 
     455        """ 
     456        Return a default widget settings filename. 
     457        """ 
     458        settings_dir = self.widgetSettingsDir 
     459        class_ = type(self) 
     460        basename = "%s.%s.pck" % (class_.__module__, class_.__name__) 
     461        filename = os.path.join(settings_dir, basename) 
     462 
     463        if os.path.exists(filename): 
     464            return filename 
     465 
     466        # Try to find the old filename format ({caption}.ini) and 
     467        # copy it to the new place 
    451468        fs_encoding = sys.getfilesystemencoding() 
    452469        basename = self.captionTitle + ".ini" 
    453         filename = os.path.join( 
    454             self.widgetSettingsDir,  # is assumed to be a str in FS encoding 
     470        legacy_filename = os.path.join( 
     471            settings_dir,  # is assumed to be a str in FS encoding 
    455472            basename.encode(fs_encoding)) 
     473 
     474        if os.path.isfile(legacy_filename): 
     475            # Copy the old settings file to the new place. 
     476            shutil.copy(legacy_filename, filename) 
     477 
    456478        return filename 
    457479 
     
    729751        if self.progressBarHandler: 
    730752            self.progressBarHandler(self, 0) 
    731         self.processingStateChanged.emit(1) 
     753 
     754        if self.__progressState != 1: 
     755            self.__progressState = 1 
     756            self.processingStateChanged.emit(1) 
    732757 
    733758    def progressBarSet(self, value, processEventsFlags=QEventLoop.AllEvents): 
     
    740765        old = self.__progressBarValue 
    741766        if value > 0: 
     767            if self.__progressState != 1: 
     768                warnings.warn("progressBarSet() called without a " 
     769                              "preceding progressBarInit()", 
     770                              stacklevel=2) 
     771                self.__progressState = 1 
     772                self.processingStateChanged.emit(1) 
     773 
    742774            self.__progressBarValue = value 
    743775            usedTime = max(1, time.time() - self.startTime) 
     
    780812        if self.progressBarHandler: 
    781813            self.progressBarHandler(self, 101) 
    782         self.processingStateChanged.emit(0) 
     814 
     815        if self.__progressState != 0: 
     816            self.__progressState = 0 
     817            self.processingStateChanged.emit(0) 
    783818 
    784819    # handler must be a function, that receives 2 arguments. First is the widget instance, the second is the value between -1 and 101 
  • Orange/OrangeWidgets/OWDatabasesUpdate.py

    r11514 r11619  
    548548 
    549549    def UpdateInfoLabel(self): 
    550         local = [item for item, _, _ in self.updateItems 
    551                  if item.state != AVAILABLE] 
    552         onServer = [item for item, _, _ in self.updateItems] 
    553  
    554         size = sum(float(special_tags(item).get("#uncompressed", item.size)) 
    555                    for item in local) 
    556  
    557         sizeOnServer = sum(float(item.size) for item, _, _ in self.updateItems) 
    558  
    559         if self.showAll: 
    560  
    561             text = ("%i items, %s (data on server: %i items, %s)" % 
    562                     (len(local), 
    563                      sizeof_fmt(size), 
    564                      len(onServer), 
    565                      sizeof_fmt(sizeOnServer))) 
    566         else: 
    567             text = "%i items, %s" % (len(local), sizeof_fmt(size)) 
     550        local = [item for item, tree_item, _ in self.updateItems 
     551                 if item.state != AVAILABLE and not tree_item.isHidden() ] 
     552        size = sum(float(item.size) for item in local) 
     553 
     554        onServer = [item for item, tree_item, _ in self.updateItems if not tree_item.isHidden()] 
     555        sizeOnServer = sum(float(item.size) for item in onServer) 
     556 
     557        text = ("%i items, %s (on server: %i items, %s)" % 
     558                (len(local), 
     559                 sizeof_fmt(size), 
     560                 len(onServer), 
     561                 sizeof_fmt(sizeOnServer))) 
    568562 
    569563        self.infoLabel.setText(text) 
     
    587581                           for string in strings) 
    588582            tree_item.setHidden(hide) 
     583        self.UpdateInfoLabel() 
    589584 
    590585    def SubmitDownloadTask(self, domain, filename): 
  • Orange/OrangeWidgets/Regression/OWRandomForestRegression.py

    r11096 r11628  
    55<contact>Marko Toplak (marko.toplak(@at@)gmail.com)</contact> 
    66<priority>320</priority> 
    7 <keywords>bagging, ensemble</keywords> 
     7<tags>bagging,ensemble</tags> 
    88 
    99""" 
     
    1212 
    1313class OWRandomForestRegression(OWRandomForest): 
    14     def __init__(self, parent=None, signalManager=None, title="Random forest regression"): 
     14    def __init__(self, parent=None, signalManager=None, 
     15                 title="Random forest regression"): 
    1516        OWRandomForest.__init__(self, parent, signalManager, title) 
    16          
     17 
    1718        self.inputs = [("Data", ExampleTable, self.setData), 
    1819                       ("Preprocess", PreprocessedLearner, self.setPreprocessor)] 
    19          
     20 
    2021        self.outputs = [("Learner", orange.Learner), 
    21                         ("Random Forest Classifier", orange.Classifier)] 
     22                        ("Random Forest Classifier", orange.Classifier), 
     23                        ("Selected Tree", Orange.classification.tree.TreeClassifier)] 
    2224 
    2325    def setData(self, data): 
    24         self.data = self.isDataWithClass(data, orange.VarTypes.Continuous, checkMissing=True) and data or None 
    25          
     26        if not self.isDataWithClass(data, orange.VarTypes.Continuous, 
     27                                    checkMissing=True): 
     28            data = None 
     29        self.data = data 
     30 
     31        self.streeEnabled(False) 
    2632        if self.data: 
    2733            learner = self.constructLearner() 
     
    3137                self.classifier = learner(self.data) 
    3238                self.classifier.name = self.name 
     39                self.streeEnabled(True) 
    3340            except Exception, (errValue): 
    3441                self.error(str(errValue)) 
     
    3946 
    4047        self.send("Random Forest Classifier", self.classifier) 
    41          
  • Orange/classification/logreg.py

    r11459 r11605  
    10271027 
    10281028class LibLinearLogRegLearner(Orange.core.LinearLearner): 
    1029     """A logistic regression learner from `LIBLINEAR`_. 
     1029    """ 
     1030    A logistic regression learner from `LIBLINEAR`_. 
    10301031 
    10311032    Supports L2 regularized learning. 
    10321033 
    10331034    .. _`LIBLINEAR`: http://www.csie.ntu.edu.tw/~cjlin/liblinear/ 
     1035 
     1036    .. note:: 
     1037        Unlike :class:`LogRegLearner` this one supports multi-class 
     1038        classification using one vs. rest strategy. 
    10341039 
    10351040    """ 
     
    10851090 
    10861091    def __call__(self, data, weight_id=None): 
     1092        """ 
     1093        Return a classifier trained on the `data` (`weight_id` is ignored). 
     1094 
     1095        :param Orange.data.Table data: 
     1096            Training data set. 
     1097        :param int weight_id: 
     1098            Ignored. 
     1099        :rval: Orange.core.LinearClassifier 
     1100 
     1101        .. note:: 
     1102            The :class:`Orange.core.LinearClassifier` is same class as 
     1103            :class:`Orange.classification.svm.LinearClassifier`. 
     1104 
     1105        """ 
    10871106        if not isinstance(data.domain.class_var, Orange.feature.Discrete): 
    10881107            raise TypeError("Can only learn a discrete class.") 
  • Orange/classification/svm/__init__.py

    r11423 r11612  
    106106        >>> learner = svm.SVMLearner() 
    107107        >>> results = testing.cross_validation([learner], data, folds=5) 
    108         >>> print "CA:  %.4f" % scoring.CA(results)[0] 
    109         CA:  0.7908 
    110         >>> print "AUC: %.4f" % scoring.AUC(results)[0] 
    111         AUC: 0.9565 
     108        >>> print "CA:  %.2f" % scoring.CA(results)[0] 
     109        CA:  0.79 
     110        >>> print "AUC: %.2f" % scoring.AUC(results)[0] 
     111        AUC: 0.95 
    112112 
    113113    """ 
     
    601601        args = (bin_class_var, self.examples, all_sv, model) 
    602602 
    603         if isinstance(svm_classifier_type, _SVMClassifierSparse): 
     603        if issubclass(svm_classifier_type, _SVMClassifierSparse): 
    604604            args = args + (int(self.__wrapped.use_non_meta),) 
    605605 
     
    10481048        >>> svm_scores = [(score(f, table), f) for f in table.domain.features] 
    10491049        >>> for feature_score, feature in sorted(svm_scores, reverse=True): 
    1050         ...     print "%-35s: %.3f" % (feature.name, feature_score) 
    1051         pr.axis aspect ratio               : 44.263 
    1052         kurtosis about major axis          : 42.593 
    1053         max.length rectangularity          : 39.377 
    1054         radius ratio                       : 28.741 
    1055         skewness about major axis          : 26.682 
    1056         hollows ratio                      : 20.993 
    1057         compactness                        : 20.085 
    1058         elongatedness                      : 17.410 
    1059         distance circularity               : 14.542 
    1060         scaled radius of gyration          : 12.584 
    1061         max.length aspect ratio            : 10.686 
    1062         scatter ratio                      : 10.574 
    1063         scaled variance along minor axis   : 10.049 
    1064         circularity                        : 8.360 
    1065         pr.axis rectangularity             : 7.473 
    1066         scaled variance along major axis   : 5.731 
    1067         skewness about minor axis          : 1.368 
    1068         kurtosis about minor axis          : 0.690 
     1050        ...     print "%-35s: %.1f" % (feature.name, feature_score) 
     1051        pr.axis aspect ratio               : 44.3 
     1052        kurtosis about major axis          : 42.6 
     1053        max.length rectangularity          : 39.4 
     1054        radius ratio                       : 28.7 
     1055        ... 
    10691056 
    10701057 
  • Orange/classification/tree.py

    r11459 r11626  
    27582758    return count 
    27592759 
     2760 
     2761def _simple_tree_convert(tree, domain, training_data=None, weight_id=None): 
     2762    """ 
     2763    Convert an :class:`SimpleTreeClassifier` to a :class:`TreeClassifier`. 
     2764 
     2765    The domain used to build it must be supplied with the `domain` 
     2766    parameter. If `training_data` is not None it is split and assigned 
     2767    to the tree's nodes. 
     2768 
     2769    :param SimpleTreeClassifeir tree: 
     2770        The :class:`SimpleTreeClassifier` instance. 
     2771    :param Orange.data.Domain domain: 
     2772        The domain on which the `tree` was built. 
     2773    :param Orange.data.Table training_data: 
     2774        Optional training data do assign to the nodes of the newly 
     2775        constructed TreeClassifier. 
     2776    :param int weight_id: 
     2777        The weight (if any) used when training the `tree`. 
     2778    :rval: TreeClassifier 
     2779 
     2780    """ 
     2781    import json 
     2782    Distribution = Orange.statistics.distribution.Distribution 
     2783 
     2784    if not isinstance(tree, SimpleTreeClassifier): 
     2785        raise TypeError("SimpleTreeClassifier instance expected (got %s)" % 
     2786                        type(tree).__name__) 
     2787 
     2788    def is_discrete(var): 
     2789        return isinstance(var, Orange.feature.Discrete) 
     2790 
     2791    def is_continuous(var): 
     2792        return isinstance(var, Orange.feature.Continuous) 
     2793 
     2794    # Get the string representation as used by pickle. 
     2795    _, (tree_string, ), _ = tree.__reduce__() 
     2796    # convert it to a valid json string 
     2797    tree_string = "[ %s ]" % (tree_string.replace(" ", ",") 
     2798                              .replace("{,", "[") 
     2799                              .replace(",}", "]") 
     2800                              .rstrip(",")) 
     2801 
     2802    tree_list = json.loads(tree_string) 
     2803 
     2804    node_type, child_count, branches = tree_list 
     2805    # node_type 0 is a classifier, 1 a regression tree 
     2806    if node_type == 0 and not is_discrete(domain.class_var): 
     2807        raise ValueError 
     2808    elif node_type == 1 and not is_continuous(domain.class_var): 
     2809        raise ValueError 
     2810 
     2811    def discrete_dist(var, values): 
     2812        """ 
     2813        Create a discrete distribution containing `values`. 
     2814        """ 
     2815        dist = Distribution(var) 
     2816        for i, val in enumerate(values): 
     2817            dist.add(i, val) 
     2818        return dist 
     2819 
     2820    def continuous_dist(var, count, value): 
     2821        """ 
     2822        Create a continuous distribution with `count` points at `value`. 
     2823        """ 
     2824        dist = Distribution(var) 
     2825        dist.add(value, count) 
     2826        return dist 
     2827 
     2828    if is_discrete(domain.class_var): 
     2829        def node_distribution(values): 
     2830            return discrete_dist(domain.class_var, values) 
     2831    else: 
     2832        def node_distribution(count_valuesum): 
     2833            count, valuesum = count_valuesum 
     2834            return continuous_dist(domain.class_var, count, valuesum / count) 
     2835 
     2836    def build_tree(branch_list): 
     2837        """ 
     2838        Recursivly build a tree for a `branch_list`. 
     2839        """ 
     2840        node_type = branch_list[0] 
     2841        node = Orange.core.TreeNode() 
     2842 
     2843        if node_type in [0, 1]: 
     2844            # Internal split node 
     2845            branch_count, split_var_ind, split_val = branch_list[1:4] 
     2846            sub_branches = branch_list[4: 4 + branch_count] 
     2847            distribution = branch_list[4 + branch_count:] 
     2848            split_var = domain[split_var_ind] 
     2849 
     2850            node.distribution = node_distribution(distribution) 
     2851 
     2852            node.branches = map(build_tree, sub_branches) 
     2853 
     2854            node.branch_sizes = \ 
     2855                [sum(branch.branch_sizes or [branch.distribution.abs]) 
     2856                 for branch in node.branches] 
     2857 
     2858            if node_type == 0: 
     2859                # Discrete split node 
     2860                node.branch_descriptions = split_var.values 
     2861 
     2862                node.branch_selector = \ 
     2863                    Orange.core.ClassifierFromVarFD( 
     2864                        class_var=split_var, 
     2865                        position=split_var_ind, 
     2866                        domain=domain, 
     2867                        distribution_for_unknown=node.distribution) 
     2868 
     2869            else: 
     2870                # Continuous split node 
     2871                node.branch_descriptions = \ 
     2872                    ["<=%.3f" % split_val, ">%.3f" % split_val] 
     2873 
     2874                transformer = \ 
     2875                    Orange.feature.discretization.ThresholdDiscretizer( 
     2876                        threshold=split_val) 
     2877 
     2878                selector_var = Orange.feature.Discrete( 
     2879                    split_var.name, values=node.branch_descriptions) 
     2880 
     2881                unknown_dist = discrete_dist(selector_var, node.branch_sizes) 
     2882 
     2883                node.branch_selector = \ 
     2884                    Orange.core.ClassifierFromVarFD( 
     2885                        class_var=selector_var, 
     2886                        domain=domain, 
     2887                        position=split_var_ind, 
     2888                        transformer=transformer, 
     2889                        transform_unknowns=False, 
     2890                        distribution_for_unknown=unknown_dist) 
     2891 
     2892        elif node_type == 2: 
     2893            # Leaf predictor node 
     2894            distribution = branch_list[2:] 
     2895            node.distribution = node_distribution(distribution) 
     2896 
     2897        # Node classifier 
     2898        if is_continuous(domain.class_var): 
     2899            default_val = node.distribution.average() 
     2900        else: 
     2901            default_val = node.distribution.modus() 
     2902 
     2903        node.node_classifier = \ 
     2904            Orange.classification.ConstantClassifier( 
     2905                class_var=domain.class_var, 
     2906                default_val=default_val, 
     2907                default_distribution=node.distribution) 
     2908        return node 
     2909 
     2910    def descend_assign_instances(node, instances, splitter, weight_id=None): 
     2911        node.instances = node.examples = instances 
     2912        if len(instances): 
     2913            node.distribution = Distribution(domain.class_var, instances, 
     2914                                             weight_id) 
     2915 
     2916            node.node_classifier = \ 
     2917                Orange.classification.majority.MajorityLearner( 
     2918                    instances, weight_id) 
     2919 
     2920        if node.branches: 
     2921            split_instances, weights = splitter(node, instances, weight_id) 
     2922 
     2923            if weights is None: 
     2924                weights = [None] * len(node.branches) 
     2925 
     2926            for branch, branch_instances, weight_id in \ 
     2927                    zip(node.branches, split_instances, weights): 
     2928                descend_assign_instances(branch, branch_instances, splitter, 
     2929                                         weight_id) 
     2930 
     2931    tree_root = build_tree(branches) 
     2932 
     2933    if training_data: 
     2934        splitter = Splitter_UnknownsAsSelector() 
     2935        descend_assign_instances(tree_root, training_data, splitter, weight_id) 
     2936 
     2937    tree_c = _TreeClassifier(domain=domain, class_var=domain.class_var) 
     2938    tree_c.descender = Orange.core.TreeDescender_UnknownMergeAsSelector() 
     2939    tree_c.tree = tree_root 
     2940    return TreeClassifier(base_classifier=tree_c) 
  • Orange/data/preprocess/scaling.py

    r10913 r11633  
    555555 
    556556 
     557def jitter_array(array, ratio=0.01, axis=0, rand_seed=None): 
     558    """ 
     559    """ 
     560    array = numpy.array(array) 
     561    shape = array.shape 
     562 
     563    if array.ndim == 1: 
     564        if axis != 0: 
     565            raise ValueError("Invalid axis") 
     566        array = array.reshape((-1, 1)) 
     567 
     568    if array.ndim > 2: 
     569        raise ValueError("'array' must be at most 2 dimensional.") 
     570 
     571    axis_min = array.min(axis=axis) 
     572    axis_max = array.max(axis=axis) 
     573    axis_span = axis_max - axis_min 
     574 
     575    # roll axis to front 
     576    array = numpy.rollaxis(array, axis, 0) 
     577 
     578    random = numpy.random.RandomState(rand_seed) 
     579    for i, span in enumerate(axis_span): 
     580        array[:, i] += random.uniform(-ratio * span / 2, ratio * span / 2, 
     581                                      array.shape[0]) 
     582 
     583    # roll axis back to its original position 
     584    array = numpy.rollaxis(array, 0, axis + 1) 
     585    array = array.reshape(shape) 
     586    return array 
     587 
     588 
    557589class ScaleLinProjData(ScaleData): 
    558590    def __init__(self): 
     
    826858 
    827859        if jitter_size > 0.0: 
    828             x_positions += numpy.random.uniform(-jitter_size, jitter_size, len(x_positions)) 
    829             y_positions += numpy.random.uniform(-jitter_size, jitter_size, len(y_positions)) 
     860            positions = numpy.vstack((x_positions, y_positions)) 
     861            positions = jitter_array(positions, jitter_size / 100., axis=1, 
     862                                     rand_seed=self.jitter_seed) 
     863            x_positions, y_positions = numpy.vsplit(positions, 2) 
     864            x_positions = x_positions.ravel() 
     865            y_positions = y_positions.ravel() 
    830866 
    831867        self.last_attr_indices = attr_indices 
  • Orange/ensemble/bagging.py

    r10963 r11630  
    6969        return BaggedClassifier(classifiers=classifiers, name=self.name, \ 
    7070                    class_var=instances.domain.class_var) 
     71 
     72    def __reduce__(self): 
     73        return type(self), (self.learner,), dict(self.__dict__) 
    7174 
    7275BaggedLearner = Orange.utils.deprecated_members( 
  • Orange/ensemble/boosting.py

    r10654 r11630  
    9292        return BoostedClassifier(classifiers = classifiers, name=self.name,  
    9393            class_var=instances.domain.class_var) 
     94 
     95    def __reduce__(self): 
     96        return type(self), (self.learner,), dict(self.__dict__) 
     97 
    9498BoostedLearner = Orange.utils.deprecated_members({"examples":"instances", "classVar":"class_var", "weightId":"weigth_id", "origWeight":"orig_weight"})(BoostedLearner) 
    9599 
  • Orange/ensemble/stacking.py

    r10900 r11630  
    4646        return StackedClassifier(classifiers, meta_classifier, name=self.name) 
    4747 
     48    def __reduce__(self): 
     49        return type(self), (self.learner,), dict(self.__dict__) 
     50 
     51 
    4852class StackedClassifier: 
    4953    """ 
  • Orange/feature/selection.py

    r10709 r11647  
    33from operator import itemgetter 
    44 
     5import Orange.data 
    56import Orange.core as orange 
    67 
    78from Orange.feature.scoring import score_all 
     9 
     10 
     11def _select_features_subset(data, features): 
     12    """Select the `features` from the `data`. 
     13 
     14    .. note:: 
     15        The `features` must be a subset of the `data.domain.features`. 
     16 
     17    """ 
     18    def as_descriptor(arg): 
     19        """Ensure `arg` is an descriptor from `data.domain`""" 
     20        return data.domain[arg] 
     21 
     22    features = map(as_descriptor, features) 
     23    domain = Orange.data.Domain(features, data.domain.class_var, 
     24                                class_vars=data.domain.class_vars) 
     25    domain.add_metas(data.domain.get_metas()) 
     26    return Orange.data.Table(domain, data) 
     27 
    828 
    929def top_rated(scores, n, highest_best=True): 
    1030    """Return n top-rated features from the list of scores. 
    1131 
    12     :param scores: a list such as the one returned by 
    13       :obj:`~Orange.feature.scoring.score_all` 
    14     :type scores: list 
    15     :param n: number of features to select. 
    16     :type n: int 
     32    :param list scores: 
     33        A list such as the one returned by :func:`.score_all` 
     34    :param int n: Number of features to select. 
     35    :param bool highest_best: 
     36        If true, the features that are scored higher are preferred. 
    1737    :rtype: :obj:`list` 
    18     :param highest_best: if true, the features that are scored higher are preferred 
    19     :type highest_best: bool 
    20     """ 
    21     return [x[0] for x in \ 
    22             sorted(scores, key=itemgetter(1), reverse=True)[:n] 
    23             ] 
     38 
     39    """ 
     40    return [f for f, score in 
     41            sorted(scores, key=itemgetter(1), reverse=highest_best)[:n]] 
    2442 
    2543bestNAtts = top_rated 
     44 
    2645 
    2746def above_threshold(scores, threshold=0.0): 
     
    2948    equal to a specified threshold. 
    3049 
    31     :param scores: a list such as one returned by 
    32       :obj:`~Orange.feature.scoring.score_all` 
    33     :type scores: list 
    34     :param threshold: threshold for selection 
    35     :type threshold: float 
     50    :param list scores: 
     51        A list such as one returned by :func:`.score_all` 
     52    :param float threshold: Threshold for selection. 
    3653    :rtype: :obj:`list` 
    3754 
    3855    """ 
    39     return [x[0] for x in scores if x[1] > threshold] 
     56    return [f for f, score in scores if score >= threshold] 
    4057 
    4158 
     
    5572    :type n: int 
    5673    :rtype: :obj:`Orange.data.Table` 
    57     """ 
    58     return data.select(top_rated(scores, n) + [data.domain.classVar.name]) 
     74 
     75    """ 
     76    features = top_rated(scores, n) 
     77    return _select_features_subset(data, features) 
    5978 
    6079selectBestNAtts = select 
     
    7796    :rtype: :obj:`Orange.data.Table` 
    7897    """ 
    79     return data.select(above_threshold(scores, threshold) + \ 
    80                        [data.domain.classVar.name]) 
     98    features = above_threshold(scores, threshold) 
     99    return _select_features_subset(data, features) 
    81100 
    82101selectAttsAboveThresh = select_above_threshold 
  • Orange/testing/regression/results_reference/svm-custom-kernel.py.darwin.txt

    r10110 r11613  
    1 SVM - RBF(Euclidean) CA: 0.96 
    2 SVM - RBF(Hamming) CA: 0.886666666667 
     1SVM - RBF(Euclidean) CA: 0.97 
     2SVM - RBF(Hamming) CA: 0.87 
    33SVM - Composite CA: 0.94 
  • Orange/testing/regression/results_reference/svm-custom-kernel.py.txt

    r9954 r11613  
    1 SVM - RBF(Euclidean) CA: 0.966666666667 
    2 SVM - RBF(Hamming) CA: 0.873333333333 
     1SVM - RBF(Euclidean) CA: 0.97 
     2SVM - RBF(Hamming) CA: 0.87 
    33SVM - Composite CA: 0.94 
  • Orange/testing/regression/results_reference/svm-easy.py.txt

    r10435 r11613  
    11Name     CA        AUC 
    2 svm easy 0.83      0.96 
    3 svm      0.76      0.95 
     2svm easy 0.81      0.96 
     3svm      0.74      0.94 
  • Orange/testing/regression/results_reference/svm-linear-weights.py.txt

    r10586 r11652  
    1 ['0.0305391660', '0.0464525615', '0.0655766610', '0.1142851330', '0.1147435934', '0.1239657860', '0.1302437856', '0.1308238026', '0.1364165500', '0.1374175856', '0.1387632111', '0.1408812075', '0.1493575565', '0.1518344820', '0.1591399900', '0.1608572883', '0.1657731559', '0.1695697600', '0.1699370279', '0.1820761410', '0.1827041161', '0.1831093664', '0.1843390221', '0.1905684270', '0.1909180450', '0.1919806873', '0.1932982887', '0.2008838837', '0.2061566394', '0.2093389787', '0.2137991275', '0.2175498224', '0.2195202824', '0.2347380743', '0.2496567209', '0.2496831102', '0.2498289071', '0.2645926914', '0.2678048194', '0.2798043763', '0.3092300737', '0.3192185952', '0.3272979394', '0.3347197463', '0.3367415609', '0.3573783044', '0.3658304312', '0.3669869944', '0.3834953229', '0.3890995409', '0.4041756463', '0.4097605066', '0.4242536327', '0.4478969203', '0.4493259506', '0.4580637448', '0.4786304349', '0.4945177545', '0.5474717710', '0.5530522984', '0.5647568024', '0.5838555421', '0.5939775048', '0.5951921790', '0.5965606638', '0.6932491137', '0.6947063827', '0.7078091253', '0.8081597686', '0.8466186966', '0.8647671894', '0.9867498369', '1.0003215809', '1.0683522323', '1.2000832157', '1.4453028351', '1.9466576191', '2.2487957189', '3.2086625764'] 
     1['0.0200', '0.0229', '0.0350', '0.0416', '0.0474', '0.0484', '0.0486', '0.0529', '0.0631', '0.0673', '0.0732', '0.0742', '0.0823', '0.0825', '0.0830', '0.0845', '0.0855', '0.0865', '0.0866', '0.0877', '0.0903', '0.0944', '0.0972', '0.0975', '0.0991', '0.0994', '0.1000', '0.1046', '0.1061', '0.1137', '0.1153', '0.1240', '0.1264', '0.1266', '0.1311', '0.1315', '0.1340', '0.1344', '0.1346', '0.1404', '0.1515', '0.1525', '0.1547', '0.1608', '0.1741', '0.1837', '0.2012', '0.2043', '0.2128', '0.2158', '0.2190', '0.2270', '0.2440', '0.2452', '0.2512', '0.2568', '0.2617', '0.2707', '0.2745', '0.2768', '0.2984', '0.3021', '0.3044', '0.3261', '0.3386', '0.3396', '0.3860', '0.3896', '0.3930', '0.4490', '0.4508', '0.4552', '0.5027', '0.5147', '0.5716', '0.7365', '0.8375', '1.0701', '1.3615'] 
  • Orange/testing/regression/results_tests_20/modules_svm-custom-kernel.py.darwin.txt

    r10007 r11613  
    1 SVM - RBF(Euclidean) CA: 0.96 
    2 SVM - RBF(Hamming) CA: 0.886666666667 
     1SVM - RBF(Euclidean) CA: 0.97 
     2SVM - RBF(Hamming) CA: 0.87 
    33SVM - Composite CA: 0.94 
  • Orange/testing/regression/results_tests_20/modules_svm-custom-kernel.py.txt

    r9951 r11613  
    1 SVM - RBF(Euclidean) CA: 0.966666666667 
    2 SVM - RBF(Hamming) CA: 0.873333333333 
     1SVM - RBF(Euclidean) CA: 0.97 
     2SVM - RBF(Hamming) CA: 0.87 
    33SVM - Composite CA: 0.94 
  • Orange/testing/regression/results_tests_20/modules_svm-test.py.darwin.txt

    r10007 r11613  
    11           Name      CA     AUC 
    2 SVM - Linear      0.960   0.999 
     2SVM - Linear      0.967   0.999 
    33SVM - Poly        0.933   0.995 
    44SVM - RBF         0.967   0.999 
  • Orange/testing/regression/tests_20/modules_svm-custom-kernel.py

    r9952 r11613  
    2626tests=orngTest.crossValidation([l1, l2, l3], data, folds=5) 
    2727[ca1, ca2, ca3]=orngStat.CA(tests) 
    28 print l1.name, "CA:", ca1 
    29 print l2.name, "CA:", ca2 
    30 print l3.name, "CA:", ca3 
     28print l1.name, "CA: %.2f" % ca1 
     29print l2.name, "CA: %.2f" % ca2 
     30print l3.name, "CA: %.2f" % ca3 
  • Orange/testing/testing.py

    r11580 r11631  
    411411        """ Test learner and classifier pickling. 
    412412        """ 
     413        def clone(obj): 
     414            return pickle.loads(pickle.dumps(obj)) 
     415 
     416        cloned_learner = clone(self.learner) 
    413417        classifier = self.learner(dataset) 
    414  
    415         s = pickle.dumps(classifier) 
    416         classifier_clone = pickle.loads(s) 
     418        classifier_clone = clone(classifier) 
     419        classifier_from_cloned = cloned_learner(dataset) 
    417420 
    418421        indices = orange.MakeRandomIndices2(p0=20)(dataset) 
    419422        test = dataset.select(indices, 0) 
     423        class_var = dataset.domain.class_var 
    420424 
    421425        for ex in test: 
    422             if isinstance(dataset.domain.class_var, Orange.feature.Continuous): 
     426            prediction1 = classifier(ex, orange.GetValue) 
     427            prediction2 = classifier_clone(ex, orange.GetValue) 
     428            prediction3 = classifier_from_cloned(ex, orange.GetValue) 
     429 
     430            if isinstance(class_var, Orange.feature.Continuous): 
    423431                # Test to third digit after the decimal point 
    424                 self.assertAlmostEqual(classifier(ex, orange.GetValue).native(), 
    425                                        classifier_clone(ex, orange.GetValue).native(), 
    426                                        min(3, dataset.domain.class_var.number_of_decimals), 
    427                                        "Pickled and original classifier return a different value!") 
     432                self.assertAlmostEqual( 
     433                    prediction1.native(), prediction2.native(), 
     434                    min(3, class_var.number_of_decimals), 
     435                    "Pickled and original classifier return a different " 
     436                    "value!") 
     437 
     438                self.assertAlmostEqual( 
     439                    prediction1.native(), prediction3.native(), 
     440                    min(3, class_var.number_of_decimals), 
     441                    "Pickled and original learner return a different " 
     442                    "classifier!") 
    428443            else: 
    429                 self.assertEqual(classifier(ex, orange.GetValue), 
    430                                  classifier_clone(ex, orange.GetValue), 
    431                                  "Pickled and original classifier return a different value!") 
     444                self.assertEqual( 
     445                    prediction1, prediction2, 
     446                    "Pickled and original classifier return a different " 
     447                    "value!") 
     448 
     449                self.assertEqual( 
     450                    prediction1, prediction3, 
     451                    "Pickled and original learner return a different " 
     452                    "classifier!") 
    432453 
    433454 
  • Orange/testing/unit/tests/test_feature_selection.py

    r10708 r11648  
    4141    def test_above_threashold(self): 
    4242        threshold = self.scores[len(self.scores) / 2][1] 
    43         above = [item[0] for item in self.scores if item[1] > threshold] 
     43        above = [item[0] for item in self.scores if item[1] >= threshold] 
    4444         
    4545        self.assertEqual(above,  
     
    5151        self.assertEqual(above, [a.name for a in new_data.domain.attributes]) 
    5252        self.assertEqual(new_data.domain.class_var, self.data.domain.class_var) 
    53          
    54          
    55          
    56          
    57          
    58      
     53 
     54    def test_select_features_subset(self): 
     55        data = Orange.data.Table("lenses") 
     56 
     57        d1 = selection._select_features_subset(data, []) 
     58        self.assertSequenceEqual(d1.domain.features, []) 
     59        self.assertIs(d1.domain.class_var, data.domain.class_var) 
     60 
     61        d1 = selection._select_features_subset(data, [data.domain[0]]) 
     62        self.assertSequenceEqual(d1.domain.features, [data.domain[0]]) 
     63        self.assertIs(d1.domain.class_var, data.domain.class_var) 
     64 
     65        domain = Orange.data.Domain(data.domain.features[:2], 
     66                                    data.domain.class_var, 
     67                                    class_vars=[data.domain.features[2]]) 
     68        domain.add_metas({-1: data.domain.features[3]}) 
     69        data = Orange.data.Table(domain, data) 
     70 
     71        d1 = selection._select_features_subset(data, [data.domain[0]]) 
     72        self.assertSequenceEqual(d1.domain.features, [data.domain[0]]) 
     73        self.assertIs(d1.domain.class_var, data.domain.class_var) 
     74        self.assertSequenceEqual(d1.domain.class_vars, data.domain.class_vars) 
     75        self.assertEqual(d1.domain.get_metas(), data.domain.get_metas()) 
     76 
    5977if __name__ == "__main__": 
    6078    unittest.main() 
    61      
  • Orange/testing/unit/tests/test_instance.py

    r10750 r11661  
    5050        self.assertRaises(TypeError, Orange.data.Instance, (domain, ["?", "?"])) 
    5151 
     52    def test_compare(self): 
     53        # Empty domain/instance (compare equal) 
     54        domain = Orange.data.Domain([]) 
     55        self.assertEqual(Orange.data.Instance(domain, []), 
     56                         Orange.data.Instance(domain, [])) 
     57 
     58        lenses = Orange.data.Table("lenses") 
     59 
     60        inst1 = lenses[0] 
     61        inst2 = Orange.data.Instance(inst1) 
     62 
     63        self.assertEqual(inst1, inst2) 
     64 
     65        inst1[0] = "?" 
     66        self.assertNotEqual(inst1, inst2) 
     67 
     68        inst2[0] = "?" 
     69        self.assertEqual(inst1, inst2) 
     70 
     71        for f in lenses.domain: 
     72            inst1[f] = inst2[f] = "?" 
     73 
     74        self.assertEqual(inst1, inst2) 
     75 
    5276 
    5377if __name__ == "__main__": 
  • Orange/testing/unit/tests/test_svm.py

    r10698 r11651  
    152152def to_sparse(data): 
    153153    domain = Orange.data.Domain([], data.domain.class_var) 
    154     domain.add_metas(dict([(Orange.core.newmetaid(), v) for v in data.domain.attributes])) 
     154    domain.add_metas(dict([(Orange.core.newmetaid(), v) 
     155                           for v in data.domain.attributes])) 
    155156    return Orange.data.Table(domain, data) 
     157 
    156158 
    157159def sparse_data_iter(): 
     
    159161        yield name, (to_sparse(data), ) 
    160162 
     163 
    161164@testing.data_driven(data_iter=sparse_data_iter()) 
    162165class SparseSVMTestCase(testing.LearnerTestCase): 
    163166    LEARNER = SVMLearnerSparse(name="svm-sparse") 
    164      
    165     @test_on_data 
    166     def test_learner_on(self, dataset): 
    167         testing.LearnerTestCase.test_learner_on(self, dataset) 
    168         svm_test_binary_classifier(self, dataset) 
     167 
     168    @test_on_data 
     169    def test_learner_on(self, dataset): 
     170        testing.LearnerTestCase.test_learner_on(self, dataset) 
     171        svm_test_binary_classifier(self, dataset) 
     172 
     173 
     174@datasets_driven(datasets=testing.CLASSIFICATION_DATASETS + 
     175                 testing.REGRESSION_DATASETS) 
     176class SparseLearnerEquivalence(unittest.TestCase): 
     177    @test_on_data 
     178    def test_sparse_learner_equivalence(self, dataset): 
     179        sparse_dataset = to_sparse(dataset) 
     180        if isinstance(dataset.domain.class_var, Orange.feature.Discrete): 
     181            svm_type = SVMLearner.C_SVC 
     182        else: 
     183            svm_type = SVMLearner.Epsilon_SVR 
     184 
     185        sparse_learner = Orange.core.SVMLearnerSparse( 
     186            probability=False, 
     187            svm_type=svm_type, 
     188            shrinking=False, 
     189            eps=1e-10 
     190        ) 
     191        dense_learner = Orange.core.SVMLearner( 
     192            probability=False, 
     193            svm_type=svm_type, 
     194            shrinking=False, 
     195            eps=1e-10 
     196        ) 
     197        sparse_cls = sparse_learner(sparse_dataset) 
     198        dense_cls = dense_learner(dataset) 
     199 
     200        for sparse, dense in zip(sparse_dataset, dataset): 
     201            val1, dist1 = sparse_cls(sparse, Orange.core.GetBoth) 
     202            val2, dist2 = dense_cls(dense, Orange.core.GetBoth) 
     203 
     204            if isinstance(dataset.domain.class_var, Orange.feature.Discrete): 
     205                for p1, p2 in zip(dist1, dist2): 
     206                    self.assertAlmostEqual(float(p1), float(p2), places=3) 
     207            else: 
     208                self.assertAlmostEqual(float(val1), float(val2), places=3) 
    169209 
    170210 
     
    224264def load_tests(loader, tests, ignore): 
    225265    import doctest 
    226     tests.addTests(doctest.DocTestSuite(svm)) 
     266    tests.addTests(doctest.DocTestSuite(svm, optionflags=doctest.ELLIPSIS)) 
    227267    return tests 
    228268 
  • Orange/utils/__init__.py

    r11497 r11653  
    686686     
    687687    if progress == True: 
    688         from Orange.utils import ConsoleProgressBar 
    689688        progress = ConsoleProgressBar("Downloading %r." % basename) 
    690689        with finishing(progress): 
  • Orange/utils/addons.py

    r11094 r11656  
    1818#TODO Document this module. 
    1919 
    20 import socket 
    2120import shelve 
    2221import xmlrpclib 
     
    2625import tempfile 
    2726import tarfile 
     27import zipfile 
    2828import shutil 
    2929import os 
     
    3131import platform 
    3232import subprocess 
     33import urllib2 
     34import urlparse 
     35import posixpath 
     36import site 
     37import itertools 
    3338 
    3439from collections import namedtuple, defaultdict 
     
    3944ADDONS_ENTRY_POINT="orange.addons" 
    4045 
    41 socket.setdefaulttimeout(120)  # In seconds. 
    4246 
    4347OrangeAddOn = namedtuple('OrangeAddOn', ['name', 'available_version', 'installed_version', 'summary', 'description', 
     
    5054 
    5155AOLIST_FILE = os.path.join(Orange.utils.environ.orange_settings_dir, "addons.shelve") 
     56 
    5257def open_addons(): 
    5358    try: 
     
    6166    return addons 
    6267 
    63 global addons_corrupted 
    64 with closing(open_addons()) as addons: 
    65     addons_corrupted = len(addons)==0 
     68 
     69def addons_corrupted(): 
     70    with closing(open_addons()) as addons: 
     71        return len(addons) == 0 
    6672 
    6773addon_refresh_callback = [] 
     
    113119        readthedocs = None 
    114120 
    115     global addons_corrupted 
    116121    docs = {} 
    117122    if progress_callback: 
     
    148153            if progress_callback: 
    149154                progress_callback(len(pkg_dict)+1, i+2) 
    150         addons_corrupted = False 
    151155 
    152156    rebuild_index() 
     
    180184 
    181185 
     186def open_archive(path, mode="r"): 
     187    """ 
     188    Return an open archive file object (zipfile.ZipFile or tarfile.TarFile). 
     189    """ 
     190    _, ext = os.path.splitext(path) 
     191    if ext == ".zip": 
     192        # TODO: should it also open .egg, ... 
     193        archive = zipfile.ZipFile(path, mode) 
     194 
     195    elif ext in (".tar", ".gz", ".bz2", ".tgz", ".tbz2", ".tb2"): 
     196        archive = tarfile.open(path, mode) 
     197 
     198    return archive 
     199 
     200 
     201member_info = namedtuple( 
     202    "member_info", 
     203    ["info",  # original info object (Tar/ZipInfo) 
     204     "path",  # filename inside the archive 
     205     "linkname",  # linkname if applicable 
     206     "issym",  # True if sym link 
     207     "islnk",  # True if hardlink 
     208     ] 
     209) 
     210 
     211 
     212def archive_members(archive): 
     213    """ 
     214    Given an open archive return an iterator of `member_info` instances. 
     215    """ 
     216    if isinstance(archive, zipfile.ZipFile): 
     217        def converter(info): 
     218            return member_info(info, info.filename, None, False, False) 
     219 
     220        return itertools.imap(converter, archive.infolist()) 
     221    elif isinstance(archive, tarfile.TarFile): 
     222        def converter(info): 
     223            return member_info(info, info.name, info.linkname, 
     224                               info.issym(), info.islnk()) 
     225        return itertools.imap(converter, archive.getmembers()) 
     226    else: 
     227        raise TypeError 
     228 
     229 
     230def resolve_path(path): 
     231    """ 
     232    Return a normalized real path. 
     233    """ 
     234    return os.path.normpath(os.path.realpath(os.path.abspath(path))) 
     235 
     236 
     237def is_badfile(member, base_dir): 
     238    """ 
     239    Would extracting `member_info` instance write outside of `base_dir`. 
     240    """ 
     241    path = member.path 
     242    full_path = resolve_path(os.path.join(base_dir, path)) 
     243    return not full_path.startswith(base_dir) 
     244 
     245 
     246def is_badlink(member, base_dir): 
     247    """ 
     248    Would extracting `member_info` instance create a link to outside 
     249    of `base_dir`. 
     250 
     251    """ 
     252    if member.issym or member.islnk: 
     253        dirname = os.path.dirname(member.path) 
     254        full_path = resolve_path(os.path.join(dirname, member.linkname)) 
     255        return not full_path.startswith(base_dir) 
     256    else: 
     257        return False 
     258 
     259 
     260def check_safe(member, base_dir): 
     261    """ 
     262    Check if member is safe to extract to base_dir or raise an exception. 
     263    """ 
     264    path = member.path 
     265    drive, path = os.path.splitdrive(path) 
     266 
     267    if drive != "": 
     268        raise ValueError("Absolute path in archive") 
     269 
     270    if path.startswith("/"): 
     271        raise ValueError("Absolute path in archive") 
     272 
     273    base_dir = resolve_path(base_dir) 
     274 
     275    if is_badfile(member, base_dir): 
     276        raise ValueError("Extract outside %r" % base_dir) 
     277    if is_badlink(member, base_dir): 
     278        raise ValueError("Link outside %r" % base_dir) 
     279 
     280    return True 
     281 
     282 
     283def extract_archive(archive, path="."): 
     284    """ 
     285    Extract the contents of `archive` to `path`. 
     286    """ 
     287    if isinstance(archive, basestring): 
     288        archive = open_archive(archive) 
     289 
     290    members = archive_members(archive) 
     291 
     292    for member in members: 
     293        if check_safe(member, path): 
     294            archive.extract(member.info, path) 
     295 
     296 
    182297def run_setup(setup_script, args): 
    183298    """ 
     
    195310        # script 
    196311        startupinfo = subprocess.STARTUPINFO() 
    197         startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW 
     312        if hasattr(subprocess, "STARTF_USESHOWWINDOW"): 
     313            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW 
     314        else: 
     315            # This flag was missing in inital releases of 2.7 
     316            startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW 
     317 
    198318        extra_kwargs["startupinfo"] = startupinfo 
    199319 
     
    207327    if progress_callback: 
    208328        progress_callback(1, 0) 
    209     import site 
     329 
     330    with closing(open_addons()) as addons: 
     331        addon = addons[name.lower()] 
     332    release_url = addon.release_url 
     333 
    210334    try: 
    211         import urllib 
    212         rh = (lambda done, bs, fs: progress_callback(fs/bs, done)) if progress_callback else None 
    213         with closing(open_addons()) as addons: 
    214             egg = urllib.urlretrieve(addons[name.lower()].release_url, reporthook=rh)[0] 
    215     except Exception, e: 
    216         raise Exception("Unable to download add-on from repository: %s" % e) 
    217  
    218     try: 
    219         try: 
    220             tmpdir = tempfile.mkdtemp() 
    221             egg_contents = tarfile.open(egg) 
    222             egg_contents.extractall(tmpdir) 
    223             with closing(open_addons()) as addons: 
    224                 setup_py = os.path.join(tmpdir, name+'-'+addons[name.lower()].available_version, 'setup.py') 
    225         except Exception, e: 
    226             raise Exception("Unable to unpack add-on: %s" % e) 
     335        tmpdir = tempfile.mkdtemp() 
     336 
     337        stream = urllib2.urlopen(release_url, timeout=120) 
     338 
     339        parsed_url = urlparse.urlparse(release_url) 
     340        package_name = posixpath.basename(parsed_url.path) 
     341        package_path = os.path.join(tmpdir, package_name) 
     342 
     343        progress_cb = (lambda value: progress_callback(value, 0)) \ 
     344                      if progress_callback else None 
     345        with open(package_path, "wb") as package_file: 
     346            Orange.utils.copyfileobj( 
     347                stream, package_file, progress=progress_cb) 
     348 
     349        extract_archive(package_path, tmpdir) 
     350 
     351        setup_py = os.path.join(tmpdir, name + '-' + addon.available_version, 
     352                                'setup.py') 
    227353 
    228354        if not os.path.isfile(setup_py): 
    229             raise Exception("Unable to install add-on - it is not properly packed.") 
     355            raise Exception("Unable to install add-on - it is not properly " 
     356                            "packed.") 
    230357 
    231358        switches = [] 
  • Orange/utils/serverfiles.py

    r11438 r11653  
    114114import socket 
    115115 
    116 # timeout in seconds 
     116# default socket timeout in seconds 
    117117timeout = 120 
    118 socket.setdefaulttimeout(timeout) 
    119118 
    120119import urllib 
     
    409408                auth = base64.encodestring('%s:%s' % (self.username, self.password))[:-1]  
    410409                request.add_header('Authorization', 'Basic %s' % auth ) # Add Auth header to request 
    411              
    412             return opener.open(request) 
    413                  
     410 
     411            return opener.open(request, timeout=timeout) 
     412 
    414413        if repeat <= 0: 
    415414            return do() 
  • distribute_setup.py

    r10818 r11618  
    1515""" 
    1616import os 
     17import shutil 
    1718import sys 
    1819import time 
     
    2021import tempfile 
    2122import tarfile 
     23import optparse 
     24 
    2225from distutils import log 
    2326 
     
    4750        return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 
    4851 
    49 DEFAULT_VERSION = "0.6.26" 
     52DEFAULT_VERSION = "0.6.48" 
    5053DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" 
    5154SETUPTOOLS_FAKED_VERSION = "0.6c11" 
     
    8588            log.warn('Something went wrong during the installation.') 
    8689            log.warn('See the error message above.') 
     90            # exitcode will be 2 
     91            return 2 
    8792    finally: 
    8893        os.chdir(old_wd) 
     94        shutil.rmtree(tmpdir) 
    8995 
    9096 
     
    111117    finally: 
    112118        os.chdir(old_wd) 
     119        shutil.rmtree(tmpdir) 
    113120    # returning the result 
    114121    log.warn(egg) 
     
    138145        try: 
    139146            import pkg_resources 
     147 
     148            # Setuptools 0.7b and later is a suitable (and preferable) 
     149            # substitute for any Distribute version. 
     150            try: 
     151                pkg_resources.require("setuptools>=0.7b") 
     152                return 
     153            except (pkg_resources.DistributionNotFound, 
     154                    pkg_resources.VersionConflict): 
     155                pass 
     156 
    140157            if not hasattr(pkg_resources, '_distribute'): 
    141158                if not no_fake: 
     
    145162            return _do_download(version, download_base, to_dir, download_delay) 
    146163        try: 
    147             pkg_resources.require("distribute>="+version) 
     164            pkg_resources.require("distribute>=" + version) 
    148165            return 
    149166        except pkg_resources.VersionConflict: 
     
    167184        if not no_fake: 
    168185            _create_fake_setuptools_pkg_info(to_dir) 
     186 
    169187 
    170188def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 
     
    204222    return os.path.realpath(saveto) 
    205223 
     224 
    206225def _no_sandbox(function): 
    207226    def __no_sandbox(*args, **kw): 
     
    228247    return __no_sandbox 
    229248 
     249 
    230250def _patch_file(path, content): 
    231251    """Will backup the file then patch it""" 
    232     existing_content = open(path).read() 
     252    f = open(path) 
     253    existing_content = f.read() 
     254    f.close() 
    233255    if existing_content == content: 
    234256        # already patched 
     
    246268_patch_file = _no_sandbox(_patch_file) 
    247269 
     270 
    248271def _same_content(path, content): 
    249     return open(path).read() == content 
     272    f = open(path) 
     273    existing_content = f.read() 
     274    f.close() 
     275    return existing_content == content 
     276 
    250277 
    251278def _rename_path(path): 
    252279    new_name = path + '.OLD.%s' % time.time() 
    253     log.warn('Renaming %s into %s', path, new_name) 
     280    log.warn('Renaming %s to %s', path, new_name) 
    254281    os.rename(path, new_name) 
    255282    return new_name 
     283 
    256284 
    257285def _remove_flat_installation(placeholder): 
     
    268296        return 
    269297 
    270     log.warn('Removing elements out of the way...') 
     298    log.warn('Moving elements out of the way...') 
    271299    pkg_info = os.path.join(placeholder, file) 
    272300    if os.path.isdir(pkg_info): 
     
    290318_remove_flat_installation = _no_sandbox(_remove_flat_installation) 
    291319 
     320 
    292321def _after_install(dist): 
    293322    log.warn('After install bootstrap.') 
    294323    placeholder = dist.get_command_obj('install').install_purelib 
    295324    _create_fake_setuptools_pkg_info(placeholder) 
     325 
    296326 
    297327def _create_fake_setuptools_pkg_info(placeholder): 
     
    308338 
    309339    log.warn('Creating %s', pkg_info) 
    310     f = open(pkg_info, 'w') 
     340    try: 
     341        f = open(pkg_info, 'w') 
     342    except EnvironmentError: 
     343        log.warn("Don't have permissions to write %s, skipping", pkg_info) 
     344        return 
    311345    try: 
    312346        f.write(SETUPTOOLS_PKG_INFO) 
     
    322356        f.close() 
    323357 
    324 _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) 
     358_create_fake_setuptools_pkg_info = _no_sandbox( 
     359    _create_fake_setuptools_pkg_info 
     360) 
     361 
    325362 
    326363def _patch_egg_dir(path): 
     
    344381_patch_egg_dir = _no_sandbox(_patch_egg_dir) 
    345382 
     383 
    346384def _before_install(): 
    347385    log.warn('Before install bootstrap.') 
     
    352390    if 'install' not in sys.argv: 
    353391        return True 
    354     args = sys.argv[sys.argv.index('install')+1:] 
     392    args = sys.argv[sys.argv.index('install') + 1:] 
    355393    for index, arg in enumerate(args): 
    356394        for option in ('--root', '--prefix'): 
     
    360398            elif arg == option: 
    361399                if len(args) > index: 
    362                     top_dir = args[index+1] 
     400                    top_dir = args[index + 1] 
    363401                    return location.startswith(top_dir) 
    364402        if arg == '--user' and USER_SITE is not None: 
     
    377415    ws = pkg_resources.working_set 
    378416    try: 
    379         setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', 
    380                                   replacement=False)) 
     417        setuptools_dist = ws.find( 
     418            pkg_resources.Requirement.parse('setuptools', replacement=False) 
     419            ) 
    381420    except TypeError: 
    382421        # old distribute API 
    383         setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) 
     422        setuptools_dist = ws.find( 
     423            pkg_resources.Requirement.parse('setuptools') 
     424        ) 
    384425 
    385426    if setuptools_dist is None: 
     
    415456        if not res: 
    416457            return 
    417     log.warn('Patched done.') 
     458    log.warn('Patching complete.') 
    418459    _relaunch() 
    419460 
     
    423464    # we have to relaunch the process 
    424465    # pip marker to avoid a relaunch bug 
    425     if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: 
     466    _cmd1 = ['-c', 'install', '--single-version-externally-managed'] 
     467    _cmd2 = ['-c', 'install', '--record'] 
     468    if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2: 
    426469        sys.argv[0] = 'setup.py' 
    427470    args = [sys.executable] + sys.argv 
     
    449492            directories.append(tarinfo) 
    450493            tarinfo = copy.copy(tarinfo) 
    451             tarinfo.mode = 448 # decimal for oct 0700 
     494            tarinfo.mode = 448  # decimal for oct 0700 
    452495        self.extract(tarinfo, path) 
    453496 
     
    475518                self._dbg(1, "tarfile: %s" % e) 
    476519 
    477 def _build_install_args(argv): 
     520 
     521def _build_install_args(options): 
     522    """ 
     523    Build the arguments to 'python setup.py install' on the distribute package 
     524    """ 
    478525    install_args = [] 
    479     user_install = '--user' in argv 
    480     if user_install and sys.version_info < (2,6): 
    481         log.warn("--user requires Python 2.6 or later") 
    482         raise SystemExit(1) 
    483     if user_install: 
     526    if options.user_install: 
     527        if sys.version_info < (2, 6): 
     528            log.warn("--user requires Python 2.6 or later") 
     529            raise SystemExit(1) 
    484530        install_args.append('--user') 
    485531    return install_args 
    486532 
    487 def main(argv, version=DEFAULT_VERSION): 
     533def _parse_args(): 
     534    """ 
     535    Parse the command line for options 
     536    """ 
     537    parser = optparse.OptionParser() 
     538    parser.add_option( 
     539        '--user', dest='user_install', action='store_true', default=False, 
     540        help='install in user site package (requires Python 2.6 or later)') 
     541    parser.add_option( 
     542        '--download-base', dest='download_base', metavar="URL", 
     543        default=DEFAULT_URL, 
     544        help='alternative URL from where to download the distribute package') 
     545    options, args = parser.parse_args() 
     546    # positional arguments are ignored 
     547    return options 
     548 
     549def main(version=DEFAULT_VERSION): 
    488550    """Install or upgrade setuptools and EasyInstall""" 
    489     tarball = download_setuptools() 
    490     _install(tarball, _build_install_args(argv)) 
    491  
     551    options = _parse_args() 
     552    tarball = download_setuptools(download_base=options.download_base) 
     553    return _install(tarball, _build_install_args(options)) 
    492554 
    493555if __name__ == '__main__': 
    494     main(sys.argv[1:]) 
     556    sys.exit(main()) 
  • docs/extend-widgets/rst/OWAttributeSampler.py

    r11085 r11593  
    55<priority>1020</priority> 
    66""" 
     7import Orange 
    78 
    89from OWWidget import * 
     
    1112class OWAttributeSampler(OWWidget): 
    1213    settingsList = [] 
    13     contextHandlers = {"": DomainContextHandler("", [ 
    14             ContextField("classAttribute", DomainContextHandler.Required), 
    15             ContextField("attributeList", DomainContextHandler.List + DomainContextHandler.SelectedRequired, 
    16                          selected="selectedAttributes")])} 
     14 
     15    # ~start context handler~ 
     16    contextHandlers = { 
     17        "": DomainContextHandler( 
     18            "", 
     19            [ContextField("classAttribute", DomainContextHandler.Required), 
     20             ContextField("attributeList", 
     21                          DomainContextHandler.List + 
     22                          DomainContextHandler.SelectedRequired, 
     23                          selected="selectedAttributes")])} 
     24    # ~end context handler~ 
    1725 
    1826    def __init__(self, parent=None, signalManager=None): 
    1927        OWWidget.__init__(self, parent, signalManager, 'AttributeSampler') 
    2028 
    21         self.inputs = [("Examples", ExampleTable, self.dataset)] 
    22         self.outputs = [("Examples", ExampleTable)] 
     29        self.inputs = [("Examples", Orange.data.Table, self.dataset)] 
     30        self.outputs = [("Examples", Orange.data.Table)] 
    2331 
    2432        self.icons = self.createAttributeIconDict() 
     
    2937        self.loadSettings() 
    3038 
    31         OWGUI.listBox(self.controlArea, self, "selectedAttributes", "attributeList", box="Selected attributes", selectionMode = QListWidget.ExtendedSelection) 
     39        OWGUI.listBox(self.controlArea, self, "selectedAttributes", 
     40                      "attributeList", 
     41                      box="Selected attributes", 
     42                      selectionMode=QListWidget.ExtendedSelection) 
     43 
    3244        OWGUI.separator(self.controlArea) 
    33         self.classAttrCombo = OWGUI.comboBox(self.controlArea, self, "classAttribute", box="Class attribute") 
     45        self.classAttrCombo = OWGUI.comboBox( 
     46            self.controlArea, self, "classAttribute", 
     47            box="Class attribute") 
     48 
    3449        OWGUI.separator(self.controlArea) 
    35         OWGUI.button(self.controlArea, self, "Commit", callback = self.outputData) 
     50        OWGUI.button(self.controlArea, self, "Commit", 
     51                     callback=self.outputData) 
    3652 
    3753        self.resize(150,400) 
     
    4359        self.classAttrCombo.clear() 
    4460        if data: 
    45             self.attributeList = [(attr.name, attr.varType) for attr in data.domain] 
     61            self.attributeList = [(attr.name, attr.varType) 
     62                                  for attr in data.domain] 
    4663            self.selectedAttributes = [] 
    4764            for attrName, attrType in self.attributeList: 
     
    6380            self.send("Examples", None) 
    6481        else: 
    65             newDomain = orange.Domain([self.data.domain[i] for i in self.selectedAttributes], self.data.domain[self.classAttribute]) 
    66             newData = orange.ExampleTable(newDomain, self.data) 
     82            newDomain = Orange.data.Domain( 
     83                [self.data.domain[i] for i in self.selectedAttributes], 
     84                self.data.domain[self.classAttribute]) 
     85 
     86            newData = Orange.data.Table(newDomain, self.data) 
    6787            self.send("Examples", newData) 
    6888 
    69  
    70 ############################################################################## 
    71 # Test the widget, run from prompt 
    7289 
    7390if __name__=="__main__": 
     
    7693    ow.show() 
    7794 
    78     data = orange.ExampleTable('iris.tab') 
     95    data = Orange.data.Table('iris.tab') 
    7996    ow.dataset(data) 
    8097 
  • docs/extend-widgets/rst/OWDataSamplerB.py

    r11085 r11593  
    22<name>Data Sampler (B)</name> 
    33<description>Randomly selects a subset of instances from the data set</description> 
    4 <icon>icons/DataSamplerB.png</icon> 
     4<icon>icons/DataSamplerB.svg</icon> 
    55<priority>20</priority> 
    66""" 
     7import Orange 
    78from OWWidget import * 
    89import OWGUI 
     
    1112    settingsList = ['proportion', 'commitOnChange'] 
    1213    def __init__(self, parent=None, signalManager=None): 
    13         OWWidget.__init__(self, parent, signalManager, 'SampleDataB') 
     14        OWWidget.__init__(self, parent, signalManager) 
    1415 
    15         self.inputs = [("Data", ExampleTable, self.data)] 
    16         self.outputs = [("Sampled Data", ExampleTable)] 
     16        self.inputs = [("Data", Orange.data.Table, self.data)] 
     17        self.outputs = [("Sampled Data", Orange.data.Table)] 
    1718 
    1819        self.proportion = 50 
     
    4950 
    5051    def selection(self): 
    51         indices = orange.MakeRandomIndices2(p0=self.proportion / 100.) 
     52        indices = Orange.data.sample.SubsetIndices2(p0=self.proportion / 100.) 
    5253        ind = indices(self.dataset) 
    5354        self.sample = self.dataset.select(ind, 0) 
     
    6162            self.commit() 
    6263 
    63 ############################################################################## 
    64 # Test the widget, run from prompt 
    6564 
    6665if __name__=="__main__": 
     
    6867    ow = OWDataSamplerB() 
    6968    ow.show() 
    70     dataset = orange.ExampleTable('iris.tab') 
     69    dataset = Orange.data.Table('iris.tab') 
    7170    ow.data(dataset) 
    7271    appl.exec_() 
  • docs/extend-widgets/rst/OWDataSamplerC.py

    r11085 r11593  
    22<name>Data Sampler (C)</name> 
    33<description>Randomly selects a subset of instances from the data set</description> 
    4 <icon>icons/DataSamplerC.png</icon> 
     4<icon>icons/DataSamplerC.svg</icon> 
    55<priority>30</priority> 
    66""" 
     7import Orange 
     8 
    79from OWWidget import * 
    810import OWGUI 
     
    1315        OWWidget.__init__(self, parent, signalManager, 'SampleDataC') 
    1416         
    15         self.inputs = [("Data", ExampleTable, self.data)] 
    16         self.outputs = [("Sampled Data", ExampleTable), ("Other Data", ExampleTable)] 
     17        self.inputs = [("Data", Orange.data.Table, self.data)] 
     18        self.outputs = [("Sampled Data", Orange.data.Table), 
     19                        ("Other Data", Orange.data.Table)] 
    1720 
    1821        self.proportion = 50 
     
    4952 
    5053    def selection(self): 
    51         indices = orange.MakeRandomIndices2(p0=self.proportion / 100.) 
     54        indices = Orange.data.sample.SubsetIndices2(p0=self.proportion / 100.) 
    5255        ind = indices(self.dataset) 
    5356        self.sample = self.dataset.select(ind, 0) 
     
    6366            self.commit() 
    6467 
    65 ############################################################################## 
    66 # Test the widget, run from prompt 
    6768 
    6869if __name__=="__main__": 
     
    7071    ow = OWDataSamplerC() 
    7172    ow.show() 
    72     dataset = orange.ExampleTable('iris.tab') 
     73    dataset = Orange.data.Table('iris.tab') 
    7374    ow.data(dataset) 
    7475    appl.exec_() 
  • docs/extend-widgets/rst/OWLearningCurveA.py

    r11085 r11593  
    22<name>Learning Curve (A)</name> 
    33<description>Takes a data set and a set of learners and shows a learning curve in a table</description> 
    4 <icon>icons/LearningCurveA.png</icon> 
     4<icon>icons/LearningCurve.svg</icon> 
    55<priority>1000</priority> 
    66""" 
    77 
     8import Orange 
     9 
    810from OWWidget import * 
    9 import OWGUI, orngTest, orngStat 
     11import OWGUI 
    1012 
    1113class OWLearningCurveA(OWWidget): 
    1214    settingsList = ["folds", "steps", "scoringF", "commitOnChange"] 
    13      
     15 
    1416    def __init__(self, parent=None, signalManager=None): 
    1517        OWWidget.__init__(self, parent, signalManager, 'LearningCurveA') 
    1618 
    17         self.inputs = [("Data", ExampleTable, self.dataset), 
    18                        ("Learner", orange.Learner, self.learner, Multiple)] 
    19          
     19        self.inputs = [("Data", Orange.data.Table, self.dataset), 
     20                       ("Learner", Orange.core.Learner, self.learner, 
     21                        Multiple)] 
     22 
    2023        self.folds = 5     # cross validation folds 
    2124        self.steps = 10    # points in the learning curve 
     
    2326        self.commitOnChange = 1 # compute curve on any change of parameters 
    2427        self.loadSettings() 
    25         self.setCurvePoints() # sets self.curvePoints, self.steps equidistantpoints from 1/self.steps to 1 
    26         self.scoring = [("Classification Accuracy", orngStat.CA), ("AUC", orngStat.AUC), ("BrierScore", orngStat.BrierScore), ("Information Score", orngStat.IS), ("Sensitivity", orngStat.sens), ("Specificity", orngStat.spec)] 
     28        self.setCurvePoints() # sets self.curvePoints, self.steps equidistant points from 1/self.steps to 1 
     29        self.scoring = [("Classification Accuracy", Orange.evaluation.scoring.CA), 
     30                        ("AUC", Orange.evaluation.scoring.AUC), 
     31                        ("BrierScore", Orange.evaluation.scoring.Brier_score), 
     32                        ("Information Score", Orange.evaluation.scoring.IS), 
     33                        ("Sensitivity", Orange.evaluation.scoring.Sensitivity), 
     34                        ("Specificity", Orange.evaluation.scoring.Specificity)] 
    2735        self.learners = [] # list of current learners from input channel, tuples (id, learner) 
    2836        self.data = None   # data on which to construct the learning curve 
     
    3644 
    3745        OWGUI.separator(self.controlArea) 
     46 
    3847        box = OWGUI.widgetBox(self.controlArea, "Evaluation Scores") 
    3948        scoringNames = [x[0] for x in self.scoring] 
    40         OWGUI.comboBox(box, self, "scoringF", items=scoringNames, callback=self.computeScores) 
     49        OWGUI.comboBox(box, self, "scoringF", items=scoringNames, 
     50                       callback=self.computeScores) 
    4151 
    4252        OWGUI.separator(self.controlArea) 
     53 
    4354        box = OWGUI.widgetBox(self.controlArea, "Options") 
    44         OWGUI.spin(box, self, 'folds', 2, 100, step=1, label='Cross validation folds:  ', 
     55        OWGUI.spin(box, self, 'folds', 2, 100, step=1, 
     56                   label='Cross validation folds:  ', 
    4557                   callback=lambda: self.computeCurve(self.commitOnChange)) 
    46         OWGUI.spin(box, self, 'steps', 2, 100, step=1, label='Learning curve points:  ', 
    47                    callback=[self.setCurvePoints, lambda: self.computeCurve(self.commitOnChange)]) 
    48  
     58        OWGUI.spin(box, self, 'steps', 2, 100, step=1, 
     59                   label='Learning curve points:  ', 
     60                   callback=[self.setCurvePoints, 
     61                             lambda: self.computeCurve(self.commitOnChange)]) 
    4962        OWGUI.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') 
    50         self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
     63        self.commitBtn = OWGUI.button(box, self, "Apply Setting", 
     64                                      callback=self.computeCurve, disabled=1) 
     65 
     66        OWGUI.rubber(self.controlArea) 
    5167 
    5268        # table widget 
    53         self.table = OWGUI.table(self.mainArea, selectionMode=QTableWidget.NoSelection) 
    54                  
     69        self.table = OWGUI.table(self.mainArea, 
     70                                 selectionMode=QTableWidget.NoSelection) 
     71 
    5572        self.resize(500,200) 
    5673 
    5774    ##############################################################################     
    58     # slots: handle input signals         
     75    # slots: handle input signals 
    5976 
    6077    def dataset(self, data): 
     
    6885            self.curves = [] 
    6986            self.scores = [] 
    70         self.commitBtn.setEnabled(self.data<>None) 
     87        self.commitBtn.setEnabled(self.data is not None) 
    7188 
    7289    def learner(self, learner, id=None): 
     
    107124            self.infob.setText("No learners.") 
    108125        self.commitBtn.setEnabled(len(self.learners)) 
    109 ##        if len(self.scores): 
     126 
    110127        if self.data: 
    111128            self.setTable() 
     
    130147    def getLearningCurve(self, learners):    
    131148        pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    132         curve = orngTest.learningCurveN(learners, self.data, folds=self.folds, proportions=self.curvePoints, callback=pb.advance) 
     149        curve = Orange.evaluation.testing.learning_curve_n( 
     150            learners, self.data, folds=self.folds, 
     151            proportions=self.curvePoints, callback=pb.advance) 
    133152        pb.finish() 
    134153        return curve 
    135154 
    136155    def setCurvePoints(self): 
    137         self.curvePoints = [(x+1.)/self.steps for x in range(self.steps)] 
     156        self.curvePoints = [(x + 1.)/self.steps for x in range(self.steps)] 
    138157 
    139158    def setTable(self): 
     
    154173            self.table.setColumnWidth(i, 80) 
    155174 
    156 ############################################################################## 
    157 # Test the widget, run from prompt 
    158175 
    159176if __name__=="__main__": 
     
    162179    ow.show() 
    163180     
    164     l1 = orange.BayesLearner() 
     181    l1 = Orange.classification.bayes.NaiveLearner() 
    165182    l1.name = 'Naive Bayes' 
    166183    ow.learner(l1, 1) 
    167184 
    168     data = orange.ExampleTable('iris.tab') 
     185    data = Orange.data.Table('iris.tab') 
    169186    ow.dataset(data) 
    170187 
    171     l2 = orange.BayesLearner() 
     188    l2 = Orange.classification.bayes.NaiveLearner() 
    172189    l2.name = 'Naive Bayes (m=10)' 
    173     l2.estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10) 
    174     l2.conditionalEstimatorConstructor = orange.ConditionalProbabilityEstimatorConstructor_ByRows(estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10)) 
     190    l2.estimatorConstructor = Orange.statistics.estimate.M(m=10) 
     191    l2.conditionalEstimatorConstructor = \ 
     192        Orange.statistics.estimate.ConditionalByRows( 
     193            estimatorConstructor = Orange.statistics.estimate.M(m=10)) 
    175194    ow.learner(l2, 2) 
    176195 
    177     import orngTree 
    178     l4 = orngTree.TreeLearner(minSubset=2) 
     196    l4 = Orange.classification.tree.TreeLearner(minSubset=2) 
    179197    l4.name = "Decision Tree" 
    180198    ow.learner(l4, 4) 
  • docs/extend-widgets/rst/OWLearningCurveB.py

    r11085 r11593  
    22<name>Learning Curve (B)</name> 
    33<description>Takes a data set and a set of learners and shows a learning curve in a table</description> 
    4 <icon>icons/LearningCurveB.png</icon> 
     4<icon>icons/LearningCurve.svg</icon> 
    55<priority>1010</priority> 
    66""" 
    77 
     8import Orange 
     9 
    810from OWWidget import * 
    9 import OWGUI, orngTest, orngStat 
     11import OWGUI 
     12 
    1013 
    1114class OWLearningCurveB(OWWidget): 
     
    1518        OWWidget.__init__(self, parent, signalManager, 'LearningCurveA') 
    1619 
    17         self.inputs = [("Train Data", ExampleTable, self.trainset, Default), 
    18                        ("Test Data", ExampleTable, self.testset), 
    19                        ("Learner", orange.Learner, self.learner, Multiple)] 
     20        self.inputs = [("Train Data", Orange.data.Table, self.trainset, Default), 
     21                       ("Test Data", Orange.data.Table, self.testset), 
     22                       ("Learner", Orange.classification.Learner, 
     23                        self.learner, Multiple)] 
    2024         
    2125        self.folds = 5     # cross validation folds 
     
    2428        self.commitOnChange = 1 # compute curve on any change of parameters 
    2529        self.loadSettings() 
    26         self.setCurvePoints() # sets self.curvePoints, self.steps equidistantpoints from 1/self.steps to 1 
    27         self.scoring = [("Classification Accuracy", orngStat.CA), ("AUC", orngStat.AUC), ("BrierScore", orngStat.BrierScore), ("Information Score", orngStat.IS), ("Sensitivity", orngStat.sens), ("Specificity", orngStat.spec)] 
     30        self.setCurvePoints() # sets self.curvePoints, self.steps equidistan points from 1/self.steps to 1 
     31        self.scoring = [("Classification Accuracy", Orange.evaluation.scoring.CA), 
     32                        ("AUC", Orange.evaluation.scoring.AUC), 
     33                        ("BrierScore", Orange.evaluation.scoring.Brier_score), 
     34                        ("Information Score", Orange.evaluation.scoring.IS), 
     35                        ("Sensitivity", Orange.evaluation.scoring.Sensitivity), 
     36                        ("Specificity", Orange.evaluation.scoring.Specificity)] 
    2837        self.learners = [] # list of current learners from input channel, tuples (id, learner) 
    2938        self.data = None   # data on which to construct the learning curve 
     
    3847 
    3948        OWGUI.separator(self.controlArea) 
     49 
    4050        box = OWGUI.widgetBox(self.controlArea, "Evaluation Scores") 
    4151        scoringNames = [x[0] for x in self.scoring] 
    42         OWGUI.comboBox(box, self, "scoringF", items=scoringNames, callback=self.computeScores) 
     52        OWGUI.comboBox(box, self, "scoringF", items=scoringNames, 
     53                       callback=self.computeScores) 
    4354 
    4455        OWGUI.separator(self.controlArea) 
     56 
    4557        box = OWGUI.widgetBox(self.controlArea, "Options") 
    46         OWGUI.spin(box, self, 'folds', 2, 100, step=1, label='Cross validation folds:  ', 
     58        OWGUI.spin(box, self, 'folds', 2, 100, step=1, 
     59                   label='Cross validation folds:  ', 
    4760                   callback=lambda: self.computeCurve(self.commitOnChange)) 
    48         OWGUI.spin(box, self, 'steps', 2, 100, step=1, label='Learning curve points:  ', 
    49                    callback=[self.setCurvePoints, lambda: self.computeCurve(self.commitOnChange)]) 
     61 
     62        OWGUI.spin(box, self, 'steps', 2, 100, step=1, 
     63                   label='Learning curve points:  ', 
     64                   callback=[self.setCurvePoints, 
     65                             lambda: self.computeCurve(self.commitOnChange)]) 
    5066 
    5167        OWGUI.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') 
    52         self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
     68        self.commitBtn = OWGUI.button(box, self, "Apply Setting", 
     69                                      callback=self.computeCurve, disabled=1) 
    5370 
    5471        # table widget 
    55         self.table = OWGUI.table(self.mainArea, selectionMode=QTableWidget.NoSelection) 
     72        self.table = OWGUI.table(self.mainArea, 
     73                                 selectionMode=QTableWidget.NoSelection) 
    5674                 
    5775        self.resize(500,200) 
    5876 
    59     ##############################################################################     
    60     # slots: handle input signals         
     77    ############################################################################## 
     78    # slots: handle input signals 
    6179 
    6280    def trainset(self, data): 
     
    7088            self.curves = [] 
    7189            self.scores = [] 
    72         self.commitBtn.setEnabled(self.data<>None) 
     90        self.commitBtn.setEnabled(self.data is not None) 
    7391 
    7492    def testset(self, testdata): 
     
    140158        pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    141159        if not self.testdata: 
    142             curve = orngTest.learningCurveN(learners, self.data, folds=self.folds, proportions=self.curvePoints, callback=pb.advance) 
    143         else: 
    144             curve = orngTest.learningCurveWithTestData(learners, 
    145               self.data, self.testdata, times=self.folds, proportions=self.curvePoints, callback=pb.advance)             
     160            curve = Orange.evaluation.testing.learning_curve_n( 
     161                learners, self.data, folds=self.folds, 
     162                proportions=self.curvePoints, 
     163                callback=pb.advance) 
     164        else: 
     165            curve = Orange.evaluation.testing.learning_curve_with_test_data( 
     166                learners, self.data, self.testdata, times=self.folds, 
     167                proportions=self.curvePoints, 
     168#                callback=pb.advance 
     169                ) 
    146170        pb.finish() 
    147171        return curve 
    148172 
    149173    def setCurvePoints(self): 
    150         self.curvePoints = [(x+1.)/self.steps for x in range(self.steps)] 
     174        self.curvePoints = [(x + 1.)/self.steps for x in range(self.steps)] 
    151175 
    152176    def setTable(self): 
     
    167191            self.table.setColumnWidth(i, 80) 
    168192 
    169 ############################################################################## 
    170 # Test the widget, run from prompt 
    171193 
    172194if __name__=="__main__": 
     
    175197    ow.show() 
    176198     
    177     l1 = orange.BayesLearner() 
     199    l1 = Orange.classification.bayes.NaiveLearner() 
    178200    l1.name = 'Naive Bayes' 
    179201    ow.learner(l1, 1) 
    180202 
    181     data = orange.ExampleTable('iris.tab') 
    182     indices = orange.MakeRandomIndices2(data, p0 = 0.7) 
     203    data = Orange.data.Table('iris.tab') 
     204    indices = Orange.data.sample.SubsetIndices2(data, p0 = 0.7) 
    183205    train = data.select(indices, 0) 
    184206    test = data.select(indices, 1) 
     
    187209    ow.testset(test) 
    188210 
    189     l2 = orange.BayesLearner() 
     211    l2 = Orange.classification.bayes.NaiveLearner() 
    190212    l2.name = 'Naive Bayes (m=10)' 
    191     l2.estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10) 
    192     l2.conditionalEstimatorConstructor = orange.ConditionalProbabilityEstimatorConstructor_ByRows(estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10)) 
     213    l2.estimatorConstructor = Orange.statistics.estimate.M(m=10) 
     214    l2.conditionalEstimatorConstructor = \ 
     215        Orange.statistics.estimate.ConditionalByRows( 
     216            estimatorConstructor = Orange.statistics.estimate.M(m=10)) 
    193217    ow.learner(l2, 2) 
    194218 
    195     import orngTree 
    196     l4 = orngTree.TreeLearner(minSubset=2) 
     219    l4 = Orange.classification.tree.TreeLearner(minSubset=2) 
    197220    l4.name = "Decision Tree" 
    198221    ow.learner(l4, 4) 
  • docs/extend-widgets/rst/OWLearningCurveC.py

    r11085 r11593  
    66""" 
    77 
     8import Orange 
     9 
    810from OWWidget import * 
    911from OWColorPalette import ColorPixmap 
    10 import OWGUI, orngTest, orngStat 
    1112from OWGraph import * 
     13 
     14import OWGUI 
    1215 
    1316import warnings 
     
    2023        OWWidget.__init__(self, parent, signalManager, 'LearningCurveC') 
    2124 
    22         self.inputs = [("Data", ExampleTable, self.dataset), 
    23                        ("Learner", orange.Learner, self.learner, Multiple)] 
     25        self.inputs = [("Data", Orange.data.Table, self.dataset), 
     26                       ("Learner", Orange.classification.Learner, 
     27                        self.learner, Multiple)] 
    2428 
    2529        self.folds = 5     # cross validation folds 
     
    3034        self.graphDrawLines = 1 # draw lines between points in the graph 
    3135        self.graphShowGrid = 1  # show gridlines in the graph 
    32         self.selectedLearners = []  
     36        self.selectedLearners = [] 
     37 
    3338        self.loadSettings() 
    3439 
    35         warnings.filterwarnings("ignore", ".*builtin attribute.*", orange.AttributeWarning) 
    36  
    37         self.setCurvePoints() # sets self.curvePoints, self.steps equidistantpoints from 1/self.steps to 1 
    38         self.scoring = [("Classification Accuracy", orngStat.CA), ("AUC", orngStat.AUC), ("BrierScore", orngStat.BrierScore), ("Information Score", orngStat.IS), ("Sensitivity", orngStat.sens), ("Specificity", orngStat.spec)] 
     40        warnings.filterwarnings("ignore", ".*builtin attribute.*", Orange.core.AttributeWarning) 
     41 
     42        self.setCurvePoints() # sets self.curvePoints, self.steps equidistant points from 1/self.steps to 1 
     43        self.scoring = [("Classification Accuracy", 
     44                         Orange.evaluation.scoring.CA), 
     45                        ("AUC", Orange.evaluation.scoring.AUC), 
     46                        ("BrierScore", Orange.evaluation.scoring.Brier_score), 
     47                        ("Information Score", Orange.evaluation.scoring.IS), 
     48                        ("Sensitivity", Orange.evaluation.scoring.sens), 
     49                        ("Specificity", Orange.evaluation.scoring.spec)] 
    3950        self.learners = [] # list of current learners from input channel, tuples (id, learner) 
    4051        self.data = None   # data on which to construct the learning curve 
     
    4960        ## class selection (classQLB) 
    5061        OWGUI.separator(self.controlArea) 
     62 
     63        # ~SPHINX start color cb~ 
    5164        self.cbox = OWGUI.widgetBox(self.controlArea, "Learners") 
    52         self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", selectionMode=QListWidget.MultiSelection, callback=self.learnerSelectionChanged) 
     65        self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", 
     66                                 selectionMode=QListWidget.MultiSelection, 
     67                                 callback=self.learnerSelectionChanged) 
    5368         
    5469        self.llb.setMinimumHeight(50) 
    5570        self.blockSelectionChanges = 0 
     71        # ~SPHINX end color cb~ 
    5672 
    5773        OWGUI.separator(self.controlArea) 
     74 
    5875        box = OWGUI.widgetBox(self.controlArea, "Evaluation Scores") 
    5976        scoringNames = [x[0] for x in self.scoring] 
     
    6279 
    6380        OWGUI.separator(self.controlArea) 
     81 
    6482        box = OWGUI.widgetBox(self.controlArea, "Options") 
    6583        OWGUI.spin(box, self, 'folds', 2, 100, step=1, 
     
    6886        OWGUI.spin(box, self, 'steps', 2, 100, step=1, 
    6987                   label='Learning curve points:  ', 
    70                    callback=[self.setCurvePoints, lambda: self.computeCurve(self.commitOnChange)]) 
     88                   callback=[self.setCurvePoints, 
     89                             lambda: self.computeCurve(self.commitOnChange)]) 
    7190 
    7291        OWGUI.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') 
    73         self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
    74  
     92        self.commitBtn = OWGUI.button(box, self, "Apply Setting", 
     93                                      callback=self.computeCurve, disabled=1) 
     94 
     95        # ~SPHINX start main area tabs~ 
    7596        # start of content (right) area 
    7697        tabs = OWGUI.tabWidget(self.mainArea) 
    7798 
    78         # graph widget 
     99        # graph tab 
    79100        tab = OWGUI.createTabPage(tabs, "Graph") 
    80101        self.graph = OWGraph(tab) 
     
    84105        self.setGraphGrid() 
    85106 
    86         # table widget 
     107        # table tab 
    87108        tab = OWGUI.createTabPage(tabs, "Table") 
    88109        self.table = OWGUI.table(tab, selectionMode=QTableWidget.NoSelection) 
     110        # ~SPHINX end main area tabs~ 
    89111 
    90112        self.resize(550,200) 
     
    187209    def getLearningCurve(self, learners): 
    188210        pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    189         curve = orngTest.learningCurveN(learners, self.data, folds=self.folds, proportions=self.curvePoints, callback=pb.advance) 
     211        curve = Orange.evaluation.testing.learning_curve_n( 
     212            learners, self.data, folds=self.folds, 
     213            proportions=self.curvePoints, 
     214            callback=pb.advance) 
     215 
    190216        pb.finish() 
    191217        return curve 
     
    250276        else: 
    251277            curve.setStyle(QwtPlotCurve.NoCurve) 
    252         curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse, \ 
    253           QBrush(QColor(0,0,0)), QPen(QColor(0,0,0)), 
    254           QSize(self.graphPointSize, self.graphPointSize))) 
     278 
     279        curve.setSymbol( 
     280            QwtSymbol(QwtSymbol.Ellipse, 
     281                      QBrush(QColor(0,0,0)), QPen(QColor(0,0,0)), 
     282                      QSize(self.graphPointSize, self.graphPointSize))) 
     283 
    255284        curve.setPen(QPen(learner.color, 5)) 
    256285 
    257286    def drawLearningCurve(self, learner): 
    258         if not self.data: return 
    259         curve = self.graph.addCurve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True) 
     287        if not self.data: 
     288            return 
     289        curve = self.graph.addCurve( 
     290            learner.name, 
     291            xData=self.curvePoints, 
     292            yData=learner.score, 
     293            autoScale=True) 
    260294         
    261295        learner.curve = curve 
     
    268302            self.drawLearningCurve(l[1]) 
    269303 
    270 ############################################################################## 
    271 # Test the widget, run from prompt 
    272304 
    273305if __name__=="__main__": 
     
    276308    ow.show() 
    277309 
    278     l1 = orange.BayesLearner() 
     310    l1 = Orange.classification.bayes.NaiveLearner() 
    279311    l1.name = 'Naive Bayes' 
    280312    ow.learner(l1, 1) 
    281313 
    282     data = orange.ExampleTable('iris.tab') 
     314    data = Orange.data.Table('iris.tab') 
    283315    ow.dataset(data) 
    284316 
    285     l2 = orange.BayesLearner() 
     317    l2 = Orange.classification.bayes.NaiveLearner() 
    286318    l2.name = 'Naive Bayes (m=10)' 
    287     l2.estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10) 
    288     l2.conditionalEstimatorConstructor = orange.ConditionalProbabilityEstimatorConstructor_ByRows(estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10)) 
    289  
    290     l3 = orange.kNNLearner(name="k-NN") 
     319    l2.estimatorConstructor = Orange.statistics.estimate.M(m=10) 
     320    l2.conditionalEstimatorConstructor = Orange.statistics.estimate.ConditionalByRows(estimatorConstructor = Orange.statistics.estimate.M(m=10)) 
     321 
     322    l3 = Orange.classification.knn.kNNLearner(name="k-NN") 
    291323    ow.learner(l3, 3) 
    292324 
    293     import orngTree 
    294     l4 = orngTree.TreeLearner(minSubset=2) 
     325    l4 = Orange.classification.tree.TreeLearner(minSubset=2) 
    295326    l4.name = "Decision Tree" 
    296327    ow.learner(l4, 4) 
     
    299330#    ow.learner(None, 2) 
    300331#    ow.learner(None, 4) 
    301      
    302  
    303332 
    304333    appl.exec_() 
  • docs/extend-widgets/rst/OWLearningCurve_plot.py

    r11085 r11593  
    7373        self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
    7474 
     75        # ~start main area tabs~ 
    7576        # start of content (right) area 
    7677        tabs = OWGUI.tabWidget(self.mainArea) 
    7778 
    78         # graph widget 
     79        # graph tab 
    7980        tab = OWGUI.createTabPage(tabs, "Graph") 
    8081        self.graph = OWPlot(tab) 
     
    8485        self.setGraphGrid() 
    8586 
    86         # table widget 
     87        # table tab 
    8788        tab = OWGUI.createTabPage(tabs, "Table") 
    8889        self.table = OWGUI.table(tab, selectionMode=QTableWidget.NoSelection) 
     90        # ~end main area tabs~ 
    8991 
    9092        self.resize(550,200) 
     
    256258 
    257259    def drawLearningCurve(self, learner): 
    258         if not self.data: return 
    259         curve = self.graph.add_curve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True) 
     260        if not self.data: 
     261            return 
     262        curve = self.graph.add_curve( 
     263            learner.name, 
     264            xData=self.curvePoints, 
     265            yData=learner.score, 
     266            autoScale=True) 
    260267         
    261268        learner.curve = curve 
  • docs/extend-widgets/rst/basics.rst

    r11439 r11593  
    1414some really nice interaction may be over 1000 lines long. 
    1515 
    16 When we have started to write this tutorial, we have been working 
    17 on widgets for quite a while. There are now (now being in the very 
    18 time this page has been crafted) about 50 widgets available, and we 
    19 have pretty much defined how widgets and their interfaces should look 
    20 like. We have also made some libraries that help set up GUI with only 
    21 a few lines of code, and some mechanisms that one may found useful and 
    22 user friendly, like progress bars and alike. 
    23  
    2416On this page, we will start with some simple essentials, and then 
    2517show how to build a simple widget that will be ready to run within 
     
    3325category has an associated priority. Opening Orange Canvas, a visual 
    3426programming environment that comes with Orange, widgets are listed in 
    35 toolbox on the top of the window: 
     27a toolbox on the left: 
    3628 
    3729.. image:: widgettoolbox.png 
    3830 
    39 By default, Orange is installed in site-packages directory of 
    40 Python libraries. Widgets are all put in the subdirectories of 
    41 OrangeWidget directory; these subdirectories define widget 
    42 categories. For instance, under windows and default settings, a 
    43 directory that stores all the widgets displayed in the Evaluate pane is 
    44 *C:\\Python23\\Lib\\site-packages\\Orange\\OrangeWidgets\\Evaluate*. Figure 
    45 above shows that at the time of writing of this text there were five 
    46 widgets for evaluation of classifiers, and this is how my Evaluate 
    47 directory looked like: 
    48  
    49 .. image:: explorer.png 
    50  
    51 Notice that there are a number of files in Evaluate directory, so 
    52 how does Orange Canvas distinguish those that define widgets? Well, 
    53 widgets are Python script files that start with a header. Here is a 
    54 header for OWTestLearners.py:: 
    55  
     31The widgets and categories to which they belong are discovered at Orange 
     32Canvas startup leveraging setuptools/distribute and it's `entry points 
     33<http://pythonhosted.org/distribute/setuptools.html#dynamic-discovery-of-services-and-plugins>`_ 
     34protocol. In particular Orange Canvas looks for widgets using a 
     35`orange.widgets` entry point. 
     36 
     37 
     38First we will examine an existing widget in Orange. The Test Learners 
     39widget which is implemented in `OWTestLearners.py 
     40<http://orange.biolab.si/trac/browser/orange/Orange/OrangeWidgets/Evaluate/OWTestLearners.py>`_. 
     41 
     42Here is its header:: 
     43 
     44    """ 
    5645    <name>Test Learners</name> 
    5746    <description>Estimates the predictive performance of learners on a data set.</description> 
    58     <icon>icons/TestLearners.png</icon> 
     47    <icon>icons/TestLearners1.svg</icon> 
    5948    <priority>200</priority> 
    60  
    61 OWTestLearners is a Python script, so the header information we 
     49    """ 
     50 
     51OWTestLearners is a Python module, so the header information we 
    6252show about lies within the comment block, with triple quote opening 
    6353and closing the comment. Header defines the name of the widget, its 
     
    6555icon, and a number expressing the priority of the widget. The name of 
    6656the widget as given in the header will be the one that will be used 
    67 throughout in Orange Canvas. As for naming, the actual file name of 
    68 the widget is not important. The description of the widget is shown 
    69 once mouse rests on an toolbar icon representing the widget. And for 
     57throughout in Orange Canvas. The description of the widget is shown 
     58once mouse rests on an toolbox icon representing the widget. And for 
    7059the priority: this determines the order in which widgets appear in the 
    71 toolbox. The one shown above for Evaluate groups has widget named Test 
    72 Learners with priority 200, Classifications with 300, ROC Analysis 
    73 with 1010, Lift Curve with 1020 and Calibration Plot with 1030. Notice 
    74 that every time the priority number crosses a multiplier of a 1000, 
    75 there is a gap in the toolbox between the widgets; in this way, a 
    76 subgroups of the widgets within the same group can be imposed. 
     60toolbox within a category. 
    7761 
    7862Widgets communicate. They use typed channels, and exchange 
     
    8468    self.outputs = [("Evaluation Results", orngTest.ExperimentResults)] 
    8569 
    86 Above two lines are for Test Learners widget, so hovering with your 
    87 mouse over its icon in the widget toolbox would yield: 
    88  
    89 .. image:: mouseoverwidgetintoolbox.png 
    9070 
    9171We will go over the syntax of channel definitions later, but for 
     
    9373 
    9474   - Widgets are defined in a Python files. 
    95    - For Orange and Orange canvas to find them, they reside in subdirectories 
    96      in OrangeWidgets directory of Orange installation. The name of the 
    97      subdirectory matters, as this is the name of the widget category. Widgets 
    98      in the same directory will be grouped in the same pane of widget toolbox 
    99      in Orange Canvas. 
    100    - A file describing a widget starts with a header. This, given in sort of 
    101      XMLish style, tells about the name, short description, location of an 
    102      icon and priority of the widget. 
     75   - Widgets are registered through entry points and are discovered at 
     76     runtime. 
     77   - A python module implementing a widget starts with a header. This, given 
     78     in sort of XMLish style, tells about the name, short description, 
     79     location of an icon and priority of the widget. 
    10380   - The sole role of priority is to specify the placement (order) of widgets 
    10481     in the Orange Canvas toolbox. 
     
    10885     is seen from the outside. 
    10986 
    110 Oh, by the way. Orange caches widget descriptions to achieve a faster 
    111 startup, but this cache is automatically refreshed at startup if any change 
    112 is detected in widgets' files. 
     87.. note:: 
     88   Orange caches widget descriptions to achieve a faster startup, 
     89   but this cache is automatically refreshed at startup if any change 
     90   is detected in widgets' file. 
    11391 
    11492*********** 
     
    11997have some fun and write a widget. We will start with a very simple 
    12098one, that will receive a data set on the input and will output a data 
    121 set with 10% of the data instances. Not to mess with other widgets, we 
    122 will create a Test directory within OrangeWidgets directory, and write 
    123 the widget in a file called `OWDataSamplerA.py`: OW for Orange Widget, 
    124 DataSampler since this is what widget will be doing, and A since we 
    125 prototype a number of this widgets in our tutorial. 
    126  
    127 The script defining the OWDataSamplerA widget starts with a follwing header:: 
     99set with 10% of the data instances. We will call this widget 
     100`OWDataSamplerA.py` (OW for Orange Widget, DataSampler since this is what 
     101widget will be doing, and A since we prototype a number of this widgets 
     102in our tutorial). 
     103 
     104But first we must create a simple `python project`_ layout called *Demo*, 
     105that we will use in the rest of this tutorial. 
     106 
     107.. _`python project`: http://docs.python.org/2/distutils/examples.html#pure-python-distribution-by-package 
     108 
     109The layout should be:: 
     110 
     111   Demo/ 
     112         setup.py 
     113         orangedemo/ 
     114                     __init__.py 
     115                     OWDataSamplerA.py 
     116 
     117and the :download:`setup.py` should contain 
     118 
     119.. literalinclude:: setup.py 
     120 
     121Note that we declare our *orangedemo* package as containing widgets 
     122from an ad hoc defined category *Demo*. 
     123 
     124Following the previous example of OWTestLearners, our module defining 
     125the OWDataSamplerA widget starts with a following header:: 
    128126 
    129127    <name>Data Sampler</name> 
    130128    <description>Randomly selects a subset of instances from the data set</description> 
    131     <icon>icons/DataSamplerA.png</icon> 
     129    <icon>icons/DataSamplerA.svg</icon> 
    132130    <priority>10</priority> 
    133131 
     
    138136 
    139137Orange Widgets are all derived from the class OWWidget. The name of 
    140 the class should be match the file name, so the lines following the 
     138the class should match the file name, so the lines following the 
    141139header in our Data Sampler widget should look something like:: 
    142140 
     141    import Orange 
    143142    from OWWidget import * 
    144143    import OWGUI 
     
    147146 
    148147        def __init__(self, parent=None, signalManager=None): 
    149             OWWidget.__init__(self, parent, signalManager, 'SampleDataA') 
    150  
    151             self.inputs = [("Data", ExampleTable, self.data)] 
    152             self.outputs = [("Sampled Data", ExampleTable)] 
     148            OWWidget.__init__(self, parent, signalManager) 
     149 
     150            self.inputs = [("Data", Orange.data.Table, self.data)] 
     151            self.outputs = [("Sampled Data", Orange.data.Table)] 
    153152 
    154153            # GUI 
     
    158157            self.resize(100,50) 
    159158 
    160 In initialization, the widget calls the :obj:`__init__` function 
    161 of a base class, passing the name 'SampleData' which will, 
    162 essentially, be used for nothing else than a stem of a file for saving 
    163 the parameters of the widgets (we will regress on these somehow 
    164 latter in tutorial). Widget then defines inputs and outputs. For 
    165 input, widget defines a "Data" channel, accepting tokens of the type 
    166 orange.ExampleTable and specifying that :obj:`data` function will 
     159In initialization, the widget calls the :func:`__init__` method 
     160of a base class. Widget then defines inputs and outputs. For input, 
     161this is a *Data* channel, accepting tokens of the type 
     162:class:`Orange.data.Table` and specifying that :func:`data` method will 
    167163be used to handle them. For now, we will use a single output channel 
    168164called "Sampled Data", which will be of the same type 
    169 (orange.ExampleTable). 
    170  
    171 Notice that the types of the channels are 
    172 specified by a class name; you can use any classes here, but if your 
    173 widgets need to talk with other widgets in Orange, you will need to 
    174 check which classes are used there. Luckily, and as one of the main 
    175 design principles, there are just a few channel types that current 
    176 Orange widgets are using. 
     165(Orange.data.Table). 
     166 
     167Notice that the types of the channels are specified by a class; 
     168you can use any class here, but if your widgets need to talk with 
     169other widgets in Orange, you will need to check which classes are 
     170used there. Luckily, and as one of the main design principles, 
     171there are just a few channel types that current Orange widgets are 
     172using. 
    177173 
    178174The next four lines specify the GUI of our widget. This will be 
     
    196192 
    197193In order to complete our widget, we now need to define how will it 
    198 handle the input data. This is done in a function called 
    199 :obj:`data` (remember, we did introduce this name in the 
    200 specification of the input channel):: 
     194handle the input data. This is done in a method called :func:`data` 
     195(remember, we did introduce this name in the specification of the 
     196input channel):: 
    201197 
    202198    def data(self, dataset): 
     
    213209            self.send("Sampled Data", None) 
    214210 
    215 The function is defined within a class definition, so its first 
    216 argument has to be :obj:`self`. The second argument called 
    217 :obj:`dataset` is the token sent through the input channel which 
    218 our function needs to handle. 
     211The :obj:`dataset` argument is the token sent through the input 
     212channel which our method needs to handle. 
    219213 
    220214To handle the non-empty token, the widget updates the interface 
     
    225219"Sampled Data". 
    226220 
    227 Notice that the token can be empty (``dataset is None``), 
    228 resulting from either the sending widget to which we have connected 
    229 intentionally emptying the channel, or when the link between the two 
    230 widgets is removed. In any case, it is important that we always write 
    231 token handlers that appropriately handle the empty tokens. In our 
    232 implementation, we took care of empty input data set by appropriately 
    233 setting the GUI of a widget and sending an empty token to the 
    234 output channel. 
    235  
    236 .. 
    237    Although our widget is now ready to test, for a final touch, let's 
    238    design an icon for our widget. As specified in the widget header, we 
    239    will call it :download:`DataSamplerA.png <DataSamplerA.png>` and will 
    240    put it in icons subdirectory of OrangeWidgets directory (together with 
    241    all other icons of other widgets). 
     221Notice that the token can be empty (``None``), resulting from either 
     222the sending widget to which we have connected intentionally emptying 
     223the channel, or when the link between the two widgets is removed. 
     224In any case, it is important that we always write token handlers 
     225that appropriately handle the empty tokens. In our implementation, 
     226we took care of empty input data set by appropriately setting the 
     227GUI of a widget and sending an empty token to the output channel. 
     228 
     229 
     230Although our widget is now ready to test, for a final touch, let's 
     231design an icon for our widget. As specified in the widget header, we 
     232will call it :download:`DataSamplerA.svg <DataSamplerA.svg>` and will 
     233put it in `icons` subdirectory of `orangedemo` directory. 
     234 
     235With this we cen now go ahead and install the orangedemo package. We 
     236will do this by running :code:`python setup.py develop` command from 
     237the `Demo` directory. 
     238 
     239.. note:: 
     240   Depending on your python installation you might need 
     241   administrator/superuser privileges. 
    242242 
    243243For a test, we now open Orange Canvas. There should be a new pane in a 
    244 widget toolbox called Test (this is the name of the directory we have 
    245 used to put in our widget). If we click on this pane, it displays an 
    246 icon of our widget. Try to hoover on it to see if the header and 
    247 channel info was processed correctly: 
     244widget toolbox called Demo. If we click on this pane, it displays an 
     245icon of our widget. Try to hover on it to see if the header and channel 
     246info was processed correctly: 
    248247 
    249248.. image:: samplewidgetontoolbox.png 
    250249 
    251250Now for the real test. We put the File widget on the schema (from 
    252 Data pane), read iris.tab data set. We also put our Data Sampler widget on the pane and 
    253 open it (double click on the icon, or right-click and choose 
    254 Open): 
     251Data pane) and load the iris.tab data set. We also put our Data 
     252Sampler widget on the scheme and open it (double click on the icon, 
     253or right-click and choose Open): 
    255254 
    256255.. image:: datasamplerAempty.png 
    257256 
    258 Drag this window off the window with the widget schema of Orange 
    259 Canvas, and connect File and Data Sampler widget (click on an ouput 
    260 connector - green box - of the File widget, and drag the line to the 
    261 input connector of the Data Sampler). If everything is ok, as soon as 
    262 you release the mouse the connection is established and, the token 
    263 that was waiting on the output of the file widget was sent to the Data 
    264 Sampler widget, which in turn updated its window: 
     257Now connect the File and Data Sampler widget (click on an output 
     258connector of the File widget, and drag the line to the input connector 
     259of the Data Sampler). If everything is ok, as soon as you release the 
     260mouse, the connection is established and, the token that was waiting 
     261on the output of the file widget was sent to the Data Sampler widget, 
     262which in turn updated its window: 
    265263 
    266264.. image:: datasamplerAupdated.png 
     
    292290        ow = OWDataSamplerA() 
    293291        ow.show() 
    294         dataset = orange.ExampleTable('iris.tab') 
     292        dataset = Orange.data.Table('iris.tab') 
    295293        ow.data(dataset) 
    296294        appl.exec_() 
    297295 
    298296These are essentially some calls to Qt routines that run GUI for our 
    299 widgets. At the core, however, notice that instead of sending the 
    300 token to the input channel, we directly called the routine for token 
    301 handling (:obj:`data`). 
    302  
    303 To test your widget in more complex environment, that for instance 
    304 requires to set a complex schema in which your widget collaborates, 
    305 use Orange Canvas to set the schema and then either 1) save the schema 
    306 to be opened every time you run Orange Canvas, or 2) save this schema 
    307 (File menu) as an application within a single file you will need to 
    308 run each time you will test your widget. 
     297widgets. Notice that we call the :func:`data` method directly. 
  • docs/extend-widgets/rst/channels.rst

    r11439 r11593  
    1818******************** 
    1919 
    20 First, I do not like the name, but can't make up anything better. In 
    21 essence, the basic idea about "multi-input" channels is that they can 
     20In essence, the basic idea about "multi-input" channels is that they can 
    2221be used to connect them with several output channels. That is, if a 
    2322widget supports such a channel, several widgets can feed their input 
     
    2524 
    2625Say we want to build a widget that takes a data set and test 
    27 various predictive modelling techniques on it. A widget has to have an 
     26various predictive modeling techniques on it. A widget has to have an 
    2827input data channel, and this we know how to deal with from our 
    2928:doc:`previous <settings>` lesson. But, somehow differently, we 
     
    5049widget are defined by:: 
    5150 
    52     self.inputs = [("Data", ExampleTable, self.dataset), 
    53                    ("Learner", orange.Learner, self.learner, Multiple + Default)] 
     51    self.inputs = [("Data", Orange.data.Table, self.dataset), 
     52                   ("Learner", Orange.classification.Learner, 
     53                    self.learner, Multiple + Default)] 
    5454 
    5555Notice that everything is pretty much the same as it was with 
     
    6363channel for its type (more on default channels later). 
    6464 
    65 .. note:: :obj:`Default` flag here is used for illustration. Since *Learner* 
    66           channel is the only channel for a :class:`orange.Learner` type 
    67           it is also the default. 
     65.. note:: 
     66   :obj:`Default` flag here is used for illustration. Since *"Learner"* 
     67   channel is the only channel for a :class:`Orange.classification.Learner` 
     68   type it is also the default. 
    6869 
    6970How does the widget know from which widget did the token come from? 
     
    7172sending the token, and having a multi-input channel only tells Orange to 
    7273send a token together with sending widget id, the two arguments with 
    73 which the receiving function is called. For our :obj:`Learner` 
    74 channel the receiving function is :obj:`learner`, and this looks 
     74which the receiving function is called. For our *"Learner"* 
     75channel the receiving function is :func:`learner`, and this looks 
    7576like the following:: 
    7677 
     
    131132(:obj:`None`). Remember that sending an empty learner 
    132133essentially means that the link with the sending widget was removed, 
    133 hance we need to remove such learner from our list. If a non-empty 
     134hence we need to remove such learner from our list. If a non-empty 
    134135learner was sent, then it is either a new learner (say, from a widget 
    135136we have just linked to our learning curve widget), or an update 
     
    149150all that is needed is the augmenting the list:: 
    150151 
    151     self.scoring = [("Classification Accuracy", orngStat.CA), 
    152                     ("AUC", orngStat.AUC), 
    153                     ("BrierScore", orngStat.BrierScore), 
    154                     ("Information Score", orngStat.IS), 
    155                     ("Sensitivity", orngStat.sens), 
    156                     ("Specificity", orngStat.spec)] 
     152    self.scoring = [("Classification Accuracy", Orange.evaluation.scoring.CA), 
     153                    ("AUC", Orange.evaluation.scoring.AUC), 
     154                    ("BrierScore", Orange.evaluation.scoring.Brier_score), 
     155                    ("Information Score", Orange.evaluation.scoring.IS), 
     156                    ("Sensitivity", Orange.evaluation.scoring.Sensitivity), 
     157                    ("Specificity", Orange.evaluation.scoring.Specificity)] 
    157158 
    158159which is defined in the initialization part of the widget. The 
     
    178179is:: 
    179180 
    180     self.outputs = [("Sampled Data", ExampleTable), ("Other Data", ExampleTable)] 
     181    self.outputs = [("Sampled Data", Orange.data.Table), 
     182                    ("Other Data", Orange.data.Table)] 
    181183 
    182184We used this in the third incarnation of :download:`data sampler widget <OWDataSamplerC.py>`, 
    183 with essentially the only other change in the code in the :obj:`selection` and 
    184 :obj:`commit` functions:: 
     185with essentially the only other change in the code in the :func:`selection` and 
     186:func:`commit` functions:: 
    185187 
    186188    def selection(self): 
    187         indices = orange.MakeRandomIndices2(p0=self.proportion / 100.) 
     189        indices = Orange.data.sample.SubsetIndices2(p0=self.proportion / 100.) 
    188190        ind = indices(self.dataset) 
    189191        self.sample = self.dataset.select(ind, 0) 
     
    197199If a widget that has multiple channels of the same type is 
    198200connected to a widget that accepts such tokens, Orange Canvas opens a 
    199 window asking the user to confirm which channels to connect. The 
    200 channel mentioned in :obj:`self.outputs` is connected by 
    201 default. Hence, if we have just connected Data Sampler 
    202 (C) widget to a Data Table widget in a schema below: 
     201window asking the user to confirm which channels to connect. Hence, 
     202if we have just connected *Data Sampler (C)* widget to a Data Table 
     203widget in a schema below: 
    203204 
    204205.. image:: datasampler-totable.png 
     
    223224training data set channel will be the default one. 
    224225 
    225 When enlisting the input channel of the same type, the non-default 
     226When enlisting the input channel of the same type, the default 
    226227channels have a special flag in the channel specification list. So for 
    227228our new :download:`learning curve <OWLearningCurveB.py>` widget, the 
    228229channel specification is:: 
    229230 
    230     self.inputs = [("Train Data", ExampleTable, self.trainset, Default), 
    231                    ("Test Data", ExampleTable, self.testset), 
    232                    ("Learner", orange.Learner, self.learner, Multiple)] 
     231    self.inputs = [("Train Data", Orange.data.Table, self.trainset, Default), 
     232                   ("Test Data", Orange.data.Table, self.testset), 
     233                   ("Learner", Orange.classification.Learner, self.learner, Multiple)] 
    233234 
    234235That is, the :obj:`Train Data` channel is a single-token 
     
    240241.. image:: file-to-learningcurveb.png 
    241242 
    242 That is, no window with a query on which channels 
    243 to connect to will open. To find out which channels got connected, 
    244 double click on the green link between the two widgets: 
    245  
    246 .. image:: file-to-learningcurveb-channels.png 
     243That is, no window with a query on which channels to connect to will 
     244open, as the default *"Train Data"* was selected. 
  • docs/extend-widgets/rst/contextsettings.rst

    r11408 r11593  
    2121selecting a subset of attributes and the class attributes (note that a 
    2222better widget for this task is already included in your Orange 
    23 instalation). 
     23installation). 
    2424 
    2525.. image:: attributesampler.png 
     
    2929somehow store the user's selection. 
    3030 
    31 Here's the widget's :obj:`__init__` function. 
    32  
    33 Part of :download:`OWAttributeSampler.py <OWAttributeSampler.py>`:: 
    34  
    35     def __init__(self, parent=None, signalManager=None): 
    36         OWWidget.__init__(self, parent, signalManager, 'AttributeSampler') 
    37  
    38         self.inputs = [("Examples", ExampleTable, self.dataset)] 
    39         self.outputs = [("Examples", ExampleTable)] 
    40  
    41         self.icons = self.createAttributeIconDict() 
    42  
    43         self.attributeList = [] 
    44         self.selectedAttributes = [] 
    45         self.classAttribute = None 
    46         self.loadSettings() 
    47  
    48         OWGUI.listBox(self.controlArea, self, "selectedAttributes", "attributeList", box="Selected attributes", selectionMode = QListWidget.ExtendedSelection) 
    49         OWGUI.separator(self.controlArea) 
    50         self.classAttrCombo = OWGUI.comboBox(self.controlArea, self, "classAttribute", box="Class attribute") 
    51         OWGUI.separator(self.controlArea) 
    52         OWGUI.button(self.controlArea, self, "Commit", callback = self.outputData) 
    53  
    54         self.resize(150,400) 
     31Here's the widget's :func:`__init__` function. 
     32 
     33Part of :download:`OWAttributeSampler.py <OWAttributeSampler.py>` 
     34 
     35.. literalinclude:: OWAttributeSampler.py 
     36   :pyobject: OWAttributeSampler.__init__ 
    5537 
    5638Note that we are strictly using controls from OWGUI. As for the 
     
    6446 
    6547When the widget gets the data, a function :obj:`dataset` is 
    66 called. 
    67  
    68 Part of :download:`OWAttributeSampler.py`:: 
     48called:: 
    6949 
    7050    def dataset(self, data): 
     
    8565 
    8666 
    87     def outputData(self): 
    88         if not self.data: 
    89             self.send("Examples", None) 
    90         else: 
    91             newDomain = orange.Domain([self.data.domain[i] for i in self.selectedAttributes], self.data.domain[self.classAttribute]) 
    92             newData = orange.ExampleTable(newDomain, self.data) 
    93             self.send("Examples", newData) 
     67.. literalinclude:: OWAttributeSampler.py 
     68   :pyobject: OWAttributeSampler.outputData 
     69 
    9470 
    9571Nothing special here (yet). We fill the list box, deselect all 
     
    11086exist in the actual domain at all. 
    11187 
    112 To make the setting dependent on the context, we put :: 
    113  
    114     contextHandlers = {"": DomainContextHandler("", [ 
    115             ContextField("classAttribute", DomainContextHandler.Required), 
    116             ContextField("attributeList", DomainContextHandler.List + 
    117                                           DomainContextHandler.SelectedRequired, 
    118                          selected="selectedAttributes")])} 
     88To make the setting dependent on the context, we put 
     89 
     90.. literalinclude:: OWAttributeSampler.py 
     91   :start-after: # ~start context handler~ 
     92   :end-before: # ~end context handler~ 
    11993 
    12094at the same place where we usually declare :obj:`settingsList`. 
     
    156130(:obj:`DomainContextHandler.Optional`); sometimes certain 
    157131attribute doesn't really matter, so if it is present in the domain, 
    158 it's gonna be used, otherwise not. And for the list, we could say 
     132it's going to be used, otherwise not. And for the list, we could say 
    159133:obj:`DomainContextHandler.List + DomainContextHandler.Required` 
    160134in which case all the attributes on the list would be required for the 
    161135domain to match. 
    162136 
    163 The default flag is :obj:`DomainContextHandler.Required`, and there are other shortcuts for declaring the context, too. The above code could be simplified as :: 
    164  
    165     contextHandlers = {"": DomainContextHandler("", [ 
    166             "classAttribute", 
    167             ContextField("attributeList", DomainContextHandler.SelectedRequiredList, 
    168                          selected="selectedAttributes")])} 
     137The default flag is :obj:`DomainContextHandler.Required`, and there 
     138are other shortcuts for declaring the context, too. The above code could 
     139be simplified as :: 
     140 
     141    contextHandlers = { 
     142        "": DomainContextHandler( 
     143            "", 
     144            ["classAttribute", 
     145             ContextField("attributeList", 
     146                          DomainContextHandler.SelectedRequiredList, 
     147                          selected="selectedAttributes")])} 
    169148 
    170149Why the dictionary and the empty string as the key? A widget can 
     
    186165function :obj:`dataset` 
    187166 
    188 Part of :download:`OWAttributeSampler.py`:: 
    189  
    190     def dataset(self, data): 
    191         self.closeContext() 
    192      
    193         self.classAttrCombo.clear() 
    194         if data: 
    195             self.attributeList = [(attr.name, attr.varType) for attr in data.domain] 
    196             self.selectedAttributes = [] 
    197             for attrName, attrType in self.attributeList: 
    198                 self.classAttrCombo.addItem(self.icons[attrType], attrName) 
    199             self.classAttribute = 0 
    200         else: 
    201             self.attributeList = [] 
    202             self.selectedAttributes = [] 
    203             self.classAttrCombo.addItem("") 
    204      
    205         self.openContext("", data) 
    206      
    207         self.data = data 
    208         self.outputData() 
    209  
    210 We added only two lines. First, before you change any controls in the widget, you need to call :obj:`self.closeContext` (the function has an optional argument, the context name, but since we use the default name, an empty string, we can omit it). This reads the data from the widget into the stored context. Then the function proceeds as before: the controls (the list box and combo box) are filled in as if there were no context handling (this is important, so once again: widget should be set up as if there were not context dependent settings). When the controls are put in a consistent state, we call :obj:`self.openContext`. The first argument is the context name and the second is the object from which the handler reads the context. In case of :obj:`DomainContextHandler` this can be either a domain or the data. :obj:`openContext` will make the context handler search through the stored context for the one that (best) matches the data, and if one is find the widget's state is set accordingly (that is, the list boxes are filled, attributes in it are selected etc.). If no context is found, a new context is established and the data from widget is copied to the context. 
     167.. literalinclude:: OWAttributeSampler.py 
     168   :pyobject: OWAttributeSampler.dataset 
     169 
     170We added only two lines. First, before you change any controls in 
     171the widget, you need to call :obj:`self.closeContext` (the function 
     172has an optional argument, the context name, but since we use the 
     173default name, an empty string, we can omit it). This reads the 
     174data from the widget into the stored context. Then the function 
     175proceeds as before: the controls (the list box and combo box) 
     176are filled in as if there were no context handling (this is 
     177important, so once again: widget should be set up as if there 
     178were not context dependent settings). When the controls are put 
     179in a consistent state, we call :obj:`self.openContext`. The first 
     180argument is the context name and the second is the object from 
     181which the handler reads the context. In case of 
     182:obj:`DomainContextHandler` this can be either a domain or the 
     183data. :obj:`openContext` will make the context handler search 
     184through the stored context for the one that (best) matches the 
     185data, and if one is find the widget's state is set accordingly 
     186(that is, the list boxes are filled, attributes in it are selected 
     187etc.). If no context is found, a new context is established and the 
     188data from widget is copied to the context. 
    211189 
    212190What can be stored as a context dependent setting? Anything, even 
  • docs/extend-widgets/rst/graphing.rst

    r11439 r11593  
    44 
    55The most fun widgets are of course those that include graphics. For 
    6 this we either use control called canvas, which is Qt's general 
    7 control for doing any graphics of choice (widgets for tree and heat map 
    8 visualizations, for instance, use this), or use a special control for 
    9 drawing data plots as provided in Qwt library and PyQwt 
     6this we either use Qt's :class:`QGraphicsScene` (widgets for tree and 
     7heat map visualizations, for instance, use this), or use a special 
     8control for drawing data plots as provided in Qwt library and :mod:`PyQwt` 
    109interface. Here we look at the latter, and extend our learning curve 
    1110widget with a control that plots the curve. 
     
    2625The widget still provides learning curve table, but this is now 
    2726offered in a tabbed pane together with a graph. The code for 
    28 definition of the tabbed pane, and initialization of the graph is:: 
     27definition of the tabbed pane, and initialization of the graph is 
    2928 
    30     # start of content (right) area 
    31     tabs = OWGUI.tabWidget(self.mainArea) 
     29.. literalinclude:: OWLearningCurveC.py 
     30   :start-after: # ~SPHINX start main area tabs~ 
     31   :end-before: # ~SPHINX end main area tabs~ 
    3232 
    33     # graph widget 
    34     tab = OWGUI.createTabPage(tabs, "Graph") 
    35     self.graph = OWGraph(tab) 
    36     self.graph.setAxisAutoScale(QwtPlot.xBottom) 
    37     self.graph.setAxisAutoScale(QwtPlot.yLeft) 
    38     tab.layout().addWidget(self.graph) 
    39     self.setGraphGrid() 
     33:class:`~OWGraph.OWGrap` is a convenience subclass of :class:`QwtPlot` 
     34and is imported from OWGraph module. For the graph, we use 
     35:func:`setAxisAutoScale` to request that the axis are automatically 
     36set in regard to the data that is plotted in the graph. We plot 
     37the graph in using the following code 
    4038 
    41 :obj:`OWGrap` is a convenience subclass of QwtPlot and is imported from 
    42 OWGraph module. For the graph, we use :obj:`setAxisAutoScale` to 
    43 request that the axis are automatically set in regard to the data that 
    44 is plotted in the graph. We plot the graph in using the following 
    45 code:: 
    46  
    47     def drawLearningCurve(self, learner): 
    48         if not self.data: return 
    49         curve = self.graph.addCurve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True) 
    50  
    51         learner.curve = curve 
    52         self.setGraphStyle(learner) 
    53         self.graph.replot() 
     39.. literalinclude:: OWLearningCurveC.py 
     40   :pyobject: OWLearningCurveC.drawLearningCurve 
    5441 
    5542This is simple. We store the curve returned from :obj:`addCurve` with a 
    56 learner, and use a trick allowed in Orange that we can simply store 
    57 this as a new attribute to the learning object. By default, Orange 
    58 would give a warning of the type:: 
    59  
    60     c:\Python23\Lib\site-packages\orange\OrangeWidgets\Test\OWLearningCurveC.py:269: 
    61      AttributeWarning: 'curve' is not a builtin attribute of 'kNNLearner' 
    62       setattr(learner, "curve", curve) 
    63  
    64 but we surpress such warnings with a line:: 
    65  
    66     warnings.filterwarnings("ignore", ".*builtin attribute.*", orange.AttributeWarning) 
    67  
     43learner. 
    6844 
    6945.. warning:: 
    7046 
    71    This is a very bad design. Please do **not** store widget data in the 
    72    input objects. 
     47   This is a very bad design. Please do **not** store widget specific 
     48   data in the input objects. 
    7349 
    7450 
    75 in the initialization part of the widget. In this way, each learner 
    76 also stores the current scores, which is a list of numbers to be 
    77 plotted in Qwt graph. The details on how the plot is set are dealt 
    78 with in :obj:`setGraphStyle` function:` :: 
     51In this way, each learner also stores the current scores, which is a 
     52list of numbers to be plotted in Qwt graph. The details on how the 
     53plot is set are dealt with in :obj:`setGraphStyle` function: 
    7954 
    80     def setGraphStyle(self, learner): 
    81         curve = learner.curve 
    82         if self.graphDrawLines: 
    83             curve.setStyle(QwtPlotCurve.Lines) 
    84         else: 
    85             curve.setStyle(QwtPlotCurve.NoCurve) 
    86         curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse, \ 
    87           QBrush(QColor(0,0,0)), QPen(QColor(0,0,0)), 
    88           QSize(self.graphPointSize, self.graphPointSize))) 
    89         curve.setPen(QPen(learner.color, 5)) 
     55.. literalinclude:: OWLearningCurveC.py 
     56   :pyobject: OWLearningCurveC.setGraphStyle 
     57 
    9058 
    9159Notice that the color of the plot line that is specific to the 
     
    10371instances of one class should be plotted in scatter plot and parallel 
    10472axis plot using the same color. Developers are thus advised to use 
    105 :obj:`ColorPaletteHSV`, which is provided as a method within 
    106 :mod:`OWWidget` module. :obj:`ColorPaletteHSV` takes an 
    107 integer as an attribute, and returns a list of corresponding number of 
     73:obj:`ColorPaletteHSV`, which can be imported from :mod:`OWWidget` 
     74module. :obj:`ColorPaletteHSV` takes an 
     75integer as an parameter, and returns a list of corresponding number of 
    10876colors. In our learning curve widget, we use it within a function that 
    109 sets the list box with learners:: 
     77sets the list box with learners 
    11078 
    111     def updatellb(self): 
    112         self.blockSelectionChanges = 1 
    113         self.llb.clear() 
    114         colors = ColorPaletteHSV(len(self.learners)) 
    115         for (i,lt) in enumerate(self.learners): 
    116             l = lt[1] 
    117             item = QListWidgetItem(ColorPixmap(colors[i]), l.name) 
    118             self.llb.addItem(item) 
    119             item.setSelected(l.isSelected) 
    120             l.color = colors[i] 
    121         self.blockSelectionChanges = 0 
     79.. literalinclude:: OWLearningCurveC.py 
     80   :pyobject: OWLearningCurveC.updatellb 
    12281 
    12382The code above sets the items of the list box, where each item 
     
    12685returned by :obj:`ColorPixmap` function defined in 
    12786:obj:`OWColorPalette.py`. Else, the classifier's list box control is 
    128 defined in the initialization of the widget using:: 
     87defined in the initialization of the widget using 
    12988 
    130     self.cbox = OWGUI.widgetBox(self.controlArea, "Learners") 
    131     self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", 
    132                              selectionMode=QListWidget.MultiSelection, 
    133                              callback=self.learnerSelectionChanged) 
    134  
    135     self.llb.setMinimumHeight(50) 
    136     self.blockSelectionChanges = 0 
     89.. literalinclude:: OWLearningCurveC.py 
     90   :start-after: # ~SPHINX start color cb~ 
     91   :end-before: # ~SPHINX end color cb~