Repository: biolab/orange3

Issue:
MDS error on tooltip
<!--
This is an issue template. Please fill in the relevant details in the
sections below.

Wrap code and verbatim terminal window output into triple backticks, see:
https://help.github.com/articles/basic-writing-and-formatting-syntax/#quoting-code

If you're raising an issue about an add-on (e.g. installed via
Options > Add-ons), raise an issue on the relevant add-on's issue
tracker instead. See: https://github.com/biolab?q=orange3
-->
Can you take a look @VesnaT ?
MDS is probably the only visualization that works without data (using distances instead) right? So this probably can't happen in other widgets?

##### Orange version
<!-- From menu _Help→About→Version_ or code `Orange.version.full_version` -->
3.19

##### Expected behavior
Tooltips don't crash Orange.


##### Actual behavior
Tooltips crash Orange.


##### Steps to reproduce the behavior
Iris into Distances (columns) into MDS. Hover over a point.


##### Additional info (worksheets, data, screenshots, ...)
```
Traceback (most recent call last):
 File "/home/lan/dev/orange3/Orange/widgets/visualize/utils/plotutils.py", line 124, in eventFilter
   return self.delegate(event)
 File "/home/lan/dev/orange3/Orange/widgets/visualize/owscatterplotgraph.py", line 1302, in help_event
   text = self.master.get_tooltip(point_data)
 File "/home/lan/dev/orange3/Orange/widgets/visualize/utils/widget.py", line 330, in get_tooltip
   for point_id in point_ids[:MAX_POINTS_IN_TOOLTIP])
 File "/home/lan/dev/orange3/Orange/widgets/visualize/utils/widget.py", line 330, in <genexpr>
   for point_id in point_ids[:MAX_POINTS_IN_TOOLTIP])
 File "/home/lan/dev/orange3/Orange/widgets/visualize/utils/widget.py", line 306, in _point_tooltip
   domain = self.data.domain
AttributeError: 'NoneType' object has no attribute 'domain'
```




Discussion:
@lanzagar I cannot replicate this. Does the issue persist?
Yes, this issue still persists.

Resolving patch:
diff --git a/Orange/widgets/unsupervised/owmds.py b/Orange/widgets/unsupervised/owmds.py
--- a/Orange/widgets/unsupervised/owmds.py
+++ b/Orange/widgets/unsupervised/owmds.py
@@ -7,7 +7,7 @@
 
 import pyqtgraph as pg
 
-from Orange.data import ContinuousVariable, Domain, Table
+from Orange.data import ContinuousVariable, Domain, Table, StringVariable
 from Orange.distance import Euclidean
 from Orange.misc import DistMatrix
 from Orange.projection.manifold import torgerson, MDS
@@ -269,8 +269,11 @@ def _initialize(self):
 
         if self.matrix is not None:
             self.effective_matrix = self.matrix
-            if self.matrix.axis == 0 and self.data is self.matrix_data:
-                self.data = None
+            if self.matrix.axis == 0 and self.data is not None \
+                    and self.data is self.matrix_data:
+                names = [[attr.name] for attr in self.data.domain.attributes]
+                domain = Domain([], metas=[StringVariable("labels")])
+                self.data = Table(domain, names)
         elif self.data.domain.attributes:
             preprocessed_data = MDS().preprocess(self.data)
             self.effective_matrix = Euclidean(preprocessed_data)
@@ -290,6 +293,12 @@ def _initialize(self):
             self.clear()
         self.graph.set_effective_matrix(self.effective_matrix)
 
+    def init_attr_values(self):
+        super().init_attr_values()
+        if self.matrix is not None and self.matrix.axis == 0 and \
+                self.data is not None and len(self.data):
+            self.attr_label = self.data.domain["labels"]
+
     def _toggle_run(self):
         if self.__state == OWMDS.Running:
             self.stop()


Test patch:
diff --git a/Orange/widgets/unsupervised/tests/test_owmds.py b/Orange/widgets/unsupervised/tests/test_owmds.py
--- a/Orange/widgets/unsupervised/tests/test_owmds.py
+++ b/Orange/widgets/unsupervised/tests/test_owmds.py
@@ -272,6 +272,22 @@ def test_saved_matrix_and_data(self):
         self.send_signal(self.widget.Inputs.data, towns_data)
         self.assertIn(towns_data.domain["label"], attr_label.model())
 
+    def test_matrix_columns_tooltip(self):
+        dist = Euclidean(self.data, axis=0)
+        self.send_signal(self.widget.Inputs.distances, dist)
+        self.assertIn("sepal length", self.widget.get_tooltip([0]))
+
+    def test_matrix_columns_labels(self):
+        dist = Euclidean(self.data, axis=0)
+        self.send_signal(self.widget.Inputs.distances, dist)
+        simulate.combobox_activate_index(self.widget.controls.attr_label, 2)
+
+    def test_matrix_columns_default_label(self):
+        dist = Euclidean(self.data, axis=0)
+        self.send_signal(self.widget.Inputs.distances, dist)
+        label_text = self.widget.controls.attr_label.currentText()
+        self.assertEqual(label_text, "labels")
+
 
 if __name__ == "__main__":
     unittest.main()