Repository: biolab/orange3

Issue:
OWBoxPlot: legible axis labels
Follow up from #2110.

Current status of Box Plot widget. Long labels illegible. Elide if labels longer than 10 chars or show just every n-th.
<img width="913" alt="Screen Shot 2019-04-12 at 11 00 09" src="https://user-images.githubusercontent.com/12524972/56025418-5f3ae300-5d12-11e9-9d99-736bdb19d394.png">

At the same time, handle reading of datetime variables better. Display datetime, not float.


Discussion:
Having more readable datetime labels would be very helpful for any kind of plot (e.g. scatter plots, line plots, etc.) 
@asmaier, #3847 and #3847 fix this for most visualizations. You'll get it in the next release.

Boxplot is a different story and it highlights a problem we have with datetime variables (which also affects other visualizations, to a lesser extent).

<img width="735" alt="Screenshot 2019-06-12 at 17 22 37" src="https://user-images.githubusercontent.com/2387315/59364351-03eb9800-8d37-11e9-81cd-8ed4c1813a32.png">

**Scale.** Numeric variables remember the (maximal) number of decimals for each variable. Datetime variables don't have this. In the above picture, we don't care about microseconds (which are currently printed out for the mean!); dates would suffice. In other cases, all events would happen in a single day, so we may print out only hours - and maybe without seconds, if they are all 00.

**Differences.** The difference between two `float`s is a `float`. The difference between two dates is not a date. Hence the variance in the above graph is printed as a number of, well, seconds. Solving this requires both, knowing the scale and being able to print out the difference - which the current `str_val` methods doesn't.

Resolving patch:
diff --git a/Orange/widgets/visualize/owboxplot.py b/Orange/widgets/visualize/owboxplot.py
--- a/Orange/widgets/visualize/owboxplot.py
+++ b/Orange/widgets/visualize/owboxplot.py
@@ -200,7 +200,8 @@ def __init__(self):
 
         self.label_txts = self.mean_labels = self.boxes = self.labels = \
             self.label_txts_all = self.attr_labels = self.order = []
-        self.scale_x = self.scene_min_x = self.scene_width = 0
+        self.scale_x = 1
+        self.scene_min_x = self.scene_max_x = self.scene_width = 0
         self.label_width = 0
 
         self.attrs = VariableListModel()
@@ -741,8 +742,7 @@ def stat_ANOVA():
 
     def mean_label(self, stat, attr, val_name):
         label = QGraphicsItemGroup()
-        t = QGraphicsSimpleTextItem(
-            "%.*f" % (attr.number_of_decimals + 1, stat.mean), label)
+        t = QGraphicsSimpleTextItem(attr.str_val(stat.mean), label)
         t.setFont(self._label_font)
         bbox = t.boundingRect()
         w2, h = bbox.width() / 2, bbox.height()
@@ -794,21 +794,26 @@ def draw_axis(self):
             self.scale_x = scale_x = viewrect.width() / (gtop - gbottom)
 
         self.scene_min_x = gbottom * scale_x
-        self.scene_width = (gtop - gbottom) * scale_x
+        self.scene_max_x = gtop * scale_x
+        self.scene_width = self.scene_max_x - self.scene_min_x
 
         val = first_val
-        decimals = max(3, 4 - int(math.log10(step)))
+        last_text = self.scene_min_x
         while True:
             l = self.box_scene.addLine(val * scale_x, -1, val * scale_x, 1,
                                        self._pen_axis_tick)
             l.setZValue(100)
-            t = self.box_scene.addSimpleText(
-                repr(round(val, decimals)) if not misssing_stats else "?",
-                self._axis_font)
-            t.setFlags(
-                t.flags() | QGraphicsItem.ItemIgnoresTransformations)
+            t = QGraphicsSimpleTextItem(
+                self.attribute.str_val(val) if not misssing_stats else "?")
+            t.setFont(self._axis_font)
+            t.setFlags(t.flags() | QGraphicsItem.ItemIgnoresTransformations)
             r = t.boundingRect()
-            t.setPos(val * scale_x - r.width() / 2, 8)
+            x_start = val * scale_x - r.width() / 2
+            x_finish = x_start + r.width()
+            if x_start > last_text + 10 and x_finish < self.scene_max_x:
+                t.setPos(x_start, 8)
+                self.box_scene.addItem(t)
+                last_text = x_finish
             if val >= top:
                 break
             val += step
@@ -875,8 +880,7 @@ def draw_axis_disc(self):
 
     def label_group(self, stat, attr, mean_lab):
         def centered_text(val, pos):
-            t = QGraphicsSimpleTextItem(
-                "%.*f" % (attr.number_of_decimals + 1, val), labels)
+            t = QGraphicsSimpleTextItem(attr.str_val(val), labels)
             t.setFont(self._label_font)
             bbox = t.boundingRect()
             t.setPos(pos - bbox.width() / 2, 22)


Test patch: