Commits

Aleš Erjavec committed a4aa7f9

Refactored widget state messages handling.

Moved the resposibility for handling messages (OWBaseWidget.widgetStateChanged)
to WidgetsScheme.

Added support for messages in base SchemeNode class.

Comments (0)

Files changed (5)

Orange/OrangeCanvas/canvas/items/nodeitem.py

 from .graphicspathobject import GraphicsPathObject
 from .utils import saturated, radial_gradient
 
+from ...scheme.node import UserMessage
 from ...registry import NAMED_COLORS
 from ...resources import icon_loader
 from .utils import uniform_linear_layout
         """
         pass
 
+    def setStateMessage(self, message):
+        """
+        Set a state message to display over the item.
+
+        Parameters
+        ----------
+        message : UserMessage
+            Message to display. `message.severity` is used to determine
+            the icon and `message.contents` is used as a tool tip.
+
+        """
+        # TODO: Group messages by message_id not by severity
+        # and deprecate set[Error|Warning|Error]Message
+        if message.severity == UserMessage.Info:
+            self.setInfoMessage(message.contents)
+        elif message.severity == UserMessage.Warning:
+            self.setWarningMessage(message.contents)
+        elif message.severity == UserMessage.Error:
+            self.setErrorMessage(message.contents)
+
     def setErrorMessage(self, message):
         if self.__error != message:
             self.__error = message

Orange/OrangeCanvas/canvas/scene.py

         node.title_changed.connect(item.setTitle)
         node.progress_changed.connect(item.setProgress)
         node.processing_state_changed.connect(item.setProcessingState)
+        node.state_message_changed.connect(item.setStateMessage)
 
         return self.add_node_item(item)
 
         node.title_changed.disconnect(item.setTitle)
         node.progress_changed.disconnect(item.setProgress)
         node.processing_state_changed.disconnect(item.setProcessingState)
+        node.state_message_changed.disconnect(item.setStateMessage)
 
         self.remove_node_item(item)
 

Orange/OrangeCanvas/document/schemeedit.py

         if self.__scheme is not scheme:
             if self.__scheme:
                 self.__scheme.title_changed.disconnect(self.titleChanged)
-                self.__scheme.node_added.disconnect(self.__onNodeAdded)
-                self.__scheme.node_removed.disconnect(self.__onNodeRemoved)
                 self.__scheme.removeEventFilter(self)
 
             self.__scheme = scheme
 
             if self.__scheme:
                 self.__scheme.title_changed.connect(self.titleChanged)
-                self.__scheme.node_added.connect(self.__onNodeAdded)
-                self.__scheme.node_removed.connect(self.__onNodeRemoved)
                 self.titleChanged.emit(scheme.title)
                 self.__cleanSettings = scheme.widget_settings()
             else:
             self.__scene.set_scheme(scheme)
 
             if self.__scheme:
-                for node in self.__scheme.nodes:
-                    self.__onNodeAdded(node)
-
                 self.__scheme.installEventFilter(self)
 
     def scheme(self):
 
             QCoreApplication.sendEvent(self, ev)
 
-    def __onNodeAdded(self, node):
-        widget = self.__scheme.widget_for_node[node]
-        widget.widgetStateChanged.connect(self.__onWidgetStateChanged)
-
-    def __onNodeRemoved(self, node):
-        widget = self.__scheme.widget_for_node[node]
-        widget.widgetStateChanged.disconnect(self.__onWidgetStateChanged)
-
-    def __onWidgetStateChanged(self, *args):
-        widget = self.sender()
-        self.scheme()
-        widget_to_node = dict(reversed(item) for item in \
-                              self.__scheme.widget_for_node.items())
-        node = widget_to_node[widget]
-        item = self.__scene.item_for_node(node)
-
-        info = widget.widgetStateToHtml(True, False, False)
-        warning = widget.widgetStateToHtml(False, True, False)
-        error = widget.widgetStateToHtml(False, False, True)
-
-        item.setInfoMessage(info or None)
-        item.setWarningMessage(warning or None)
-        item.setErrorMessage(error or None)
-
     def __onNodeActivate(self, item):
         node = self.__scene.node_for_item(item)
         widget = self.scheme().widget_for_node[node]

Orange/OrangeCanvas/scheme/node.py

 from PyQt4.QtCore import pyqtProperty as Property
 
 
+class UserMessage(object):
+    """
+    A user message that should be displayed in a scheme view.
+
+    Paramaters
+    ----------
+    contents : str
+        Message text.
+    severity : int
+        Message severity.
+    message_id : A hashable object
+        Message id.
+    data : dict
+        A dictionary with optional extra data.
+
+    """
+    #: Severity flags
+    Info, Warning, Error = 1, 2, 3
+
+    def __init__(self, contents, severity=Info, message_id=None, data={}):
+        self.contents = contents
+        self.severity = severity
+        self.message_id = message_id
+        self.data = dict(data)
+
+
 class SchemeNode(QObject):
     """
     A node in a :class:`.Scheme`.
         self.__position = position or (0, 0)
         self.__progress = -1
         self.__processing_state = 0
+        self.__state_messages = {}
         self.properties = properties or {}
 
     def input_channels(self):
     tool_tip = Property(str, fset=set_tool_tip,
                         fget=tool_tip)
 
+    def set_state_message(self, message):
+        """
+        Set a message to be displayed by a scheme view for this node.
+        """
+        if message.message_id in self.__state_messages and \
+                not message.contents:
+            del self.__state_messages[message.message_id]
+
+        self.__state_messages[message.message_id] = message
+
+        self.state_message_changed.emit(message)
+
+    #: The node's state message has changed
+    state_message_changed = Signal(UserMessage)
+
     def __str__(self):
         return u"SchemeNode(description_id=%s, title=%r, ...)" % \
                 (str(self.description.id), self.title)

Orange/OrangeCanvas/scheme/widgetsscheme.py

 
 from .signalmanager import SignalManager, compress_signals, can_enable_dynamic
 from .scheme import Scheme, SchemeNode
+from .node import UserMessage
 from .utils import name_lookup, check_arg, check_type
 from ..resources import icon_loader
 from ..config import rc
         # Bind widgets progress/processing state back to the node's properties
         widget.progressBarValueChanged.connect(node.set_progress)
         widget.processingStateChanged.connect(node.set_processing_state)
+        widget.widgetStateChanged.connect(self.__on_widget_state_changed)
         self.connect(widget,
                      SIGNAL("blockingStateChanged(bool)"),
                      self.signal_manager._update)
         return [self.widget_for_node[node].getSettings(alsoContexts=False)
                 for node in self.nodes]
 
+    def __on_widget_state_changed(self, message_type, message_id,
+                                  message_value):
+        """
+        The OWBaseWidget info/warning/error state has changed.
+
+        message_type is one of "Info", "Warning" or "Error" string depending
+        of which method (information, warning, error) was called. message_id
+        is the first int argument if supplied, and message_value the message
+        text.
+
+        """
+        widget = self.sender()
+        node = self.node_for_widget.get(widget)
+        if node is not None:
+            message_type = str(message_type)
+            if message_type == "Info":
+                contents = widget.widgetStateToHtml(True, False, False)
+                level = UserMessage.Info
+            elif message_type == "Warning":
+                contents = widget.widgetStateToHtml(False, True, False)
+                level = UserMessage.Warning
+            elif message_type == "Error":
+                contents = widget.widgetStateToHtml(False, False, True)
+                level = UserMessage.Error
+            else:
+                raise ValueError("Invalid message_type: %r" % message_type)
+
+            if not contents:
+                contents = None
+
+            message = UserMessage(contents, severity=level,
+                                  message_id=message_type,
+                                  data={"contents-type": "text/html"})
+            node.set_state_message(message)
+
     def sync_node_properties(self):
         """Sync the widget settings/properties with the SchemeNode.properties.
         Return True if there were any changes in the properties (i.e. if the