Skip to content

Commit

Permalink
OWMosaic: Wrap legend
Browse files Browse the repository at this point in the history
  • Loading branch information
janezd committed Jun 14, 2019
1 parent 102c0de commit d8f6cf6
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 38 deletions.
67 changes: 32 additions & 35 deletions Orange/widgets/visualize/owmosaic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from collections import defaultdict
from functools import reduce
from itertools import product, chain
from itertools import product, chain, repeat
from math import sqrt, log
from operator import mul, attrgetter

Expand All @@ -9,7 +9,8 @@
from scipy.special import comb
from AnyQt.QtCore import Qt, QSize, pyqtSignal as Signal
from AnyQt.QtGui import QColor, QPainter, QPen, QStandardItem
from AnyQt.QtWidgets import QGraphicsScene, QGraphicsLineItem
from AnyQt.QtWidgets import (
QGraphicsScene, QGraphicsLineItem, QGraphicsItemGroup)

from Orange.data import Table, filter, Variable, Domain
from Orange.data.sql.table import SqlTable, LARGE_TABLE, DEFAULT_SAMPLE_TIME
Expand All @@ -28,6 +29,7 @@
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.visualize.utils import (
CanvasText, CanvasRectangle, ViewWithPress, VizRankDialog)
from Orange.widgets.visualize.utils.plotutils import wrap_legend_items
from Orange.widgets.widget import OWWidget, Msg, Input, Output


Expand Down Expand Up @@ -813,42 +815,29 @@ def line(x1, y1, x2, y2):
"{}<hr>Instances: {}<br><br>{}".format(
condition, n_actual, text[:-4]))

def draw_legend(x0_x1, y0_y1):
x0, x1 = x0_x1
_, y1 = y0_y1
def create_legend():
if self.variable_color is None:
names = ["<-8", "-8:-4", "-4:-2", "-2:2", "2:4", "4:8", ">8",
"Residuals:"]
colors = self.RED_COLORS[::-1] + self.BLUE_COLORS[1:]
edges = repeat(Qt.black)
else:
names = get_variable_values_sorted(class_var) + \
[class_var.name + ":"]
colors = [QColor(*col) for col in class_var.colors]

names = [CanvasText(self.canvas, name, alignment=Qt.AlignVCenter)
for name in names]
totalwidth = sum(text.boundingRect().width() for text in names)

# compute the x position of the center of the legend
y = y1 + self.ATTR_NAME_OFFSET + self.ATTR_VAL_OFFSET + 35
distance = 30
startx = (x0 + x1) / 2 - (totalwidth + (len(names)) * distance) / 2

names[-1].setPos(startx + 15, y)
names[-1].show()
xoffset = names[-1].boundingRect().width() + distance
names = get_variable_values_sorted(class_var)
edges = colors = [QColor(*col) for col in class_var.colors]

items = []
size = 8
for i in range(len(names) - 1):
if self.variable_color is None:
edgecolor = Qt.black
else:
edgecolor = colors[i]

CanvasRectangle(self.canvas, startx + xoffset, y - size / 2,
size, size, edgecolor, colors[i])
names[i].setPos(startx + xoffset + 10, y)
xoffset += distance + names[i].boundingRect().width()
for name, color, edgecolor in zip(names, colors, edges):
item = QGraphicsItemGroup()
item.addToGroup(
CanvasRectangle(None, -size / 2, -size / 2, size, size,
edgecolor, color))
item.addToGroup(
CanvasText(None, name, size, 0, Qt.AlignVCenter))
items.append(item)
return wrap_legend_items(
items, hspacing=20, vspacing=16 + size,
max_width=self.canvas_view.width() - 2 * xoff)

self.canvas.clear()
self.areas = []
Expand Down Expand Up @@ -896,8 +885,10 @@ def get_max_label_width(attr):
maxw = max(int(t.boundingRect().width()), maxw)
return maxw

# get the maximum width of rectangle
xoff = 20
legend = create_legend()

# get the maximum width of rectangle
width = 20
max_ylabel_w1 = max_ylabel_w2 = 0
if len(attr_list) > 1:
Expand All @@ -913,10 +904,11 @@ def get_max_label_width(attr):
self.ATTR_VAL_OFFSET + max_ylabel_w2 - 10

# get the maximum height of rectangle
height = 100
yoff = 45
legendoff = yoff + self.ATTR_NAME_OFFSET + self.ATTR_VAL_OFFSET + 35
square_size = min(self.canvas_view.width() - width - 20,
self.canvas_view.height() - height - 20)
self.canvas_view.height() - legendoff
- legend.boundingRect().height())

if square_size < 0:
return # canvas is too small to draw rectangles
Expand All @@ -937,7 +929,12 @@ def get_max_label_width(attr):
draw_data(
attr_list, (xoff, xoff + square_size), (yoff, yoff + square_size),
0, "", len(attr_list), [], [])
draw_legend((xoff, xoff + square_size), (yoff, yoff + square_size))

self.canvas.addItem(legend)
legend.setPos(
xoff - legend.boundingRect().x()
+ max(0, (square_size - legend.boundingRect().width()) / 2),
legendoff + square_size)
self.update_selection_rects()

@classmethod
Expand Down
3 changes: 2 additions & 1 deletion Orange/widgets/visualize/tests/test_owmosaic.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ def assertCount(cb_color, cb_attr, areas):
assertCount(1, [0, 1, 1, 1], 0)

@patch('Orange.widgets.visualize.owmosaic.CanvasRectangle')
def test_different_number_of_attributes(self, canvas_rectangle):
@patch('Orange.widgets.visualize.owmosaic.QGraphicsItemGroup.addToGroup')
def test_different_number_of_attributes(self, _, canvas_rectangle):
domain = Domain([DiscreteVariable(c, values="01") for c in "abcd"])
data = Table.from_list(
domain,
Expand Down
42 changes: 40 additions & 2 deletions Orange/widgets/visualize/utils/plotutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
)
from AnyQt.QtGui import QTransform
from AnyQt.QtWidgets import (
QGraphicsLineItem, QGraphicsSceneMouseEvent, QPinchGesture
)
QGraphicsLineItem, QGraphicsSceneMouseEvent, QPinchGesture,
QGraphicsItemGroup)

import pyqtgraph as pg

Expand Down Expand Up @@ -348,3 +348,41 @@ def mouseDragEvent(self, ev, axis=None):
else:
self.moved.emit(self.item_id, pos.x(), pos.y())
self.graph.show_indicator(self.item_id)


def wrap_legend_items(items, max_width, hspacing, vspacing):
def line_width(line):
return sum(item.boundingRect().width() for item in line) \
+ hspacing * (len(line) - 1)

def create_line(line, yi, fixed_width=None):
x = 0
for item in line:
item.setPos(x, yi * vspacing)
paragraph.addToGroup(item)
if fixed_width:
x += fixed_width
else:
x += item.boundingRect().width() + hspacing

max_item = max(item.boundingRect().width() + hspacing for item in items)
in_line = int(max_width // max_item)
if line_width(items) < max_width: # single line
lines = [items]
fixed_width = None
elif in_line < 2:
lines = [[]]
for i, item in enumerate(items): # just a single column - free wrap
lines[-1].append(item)
if line_width(lines[-1]) > max_width and len(lines[-1]) > 1:
lines.append([lines[-1].pop()])
fixed_width = None
else: # arrange into grid
lines = [items[i:i + in_line]
for i in range(0, len(items) + in_line - 1, in_line)]
fixed_width = max_item

paragraph = QGraphicsItemGroup()
for yi, line in enumerate(lines):
create_line(line, yi, fixed_width=fixed_width)
return paragraph

0 comments on commit d8f6cf6

Please sign in to comment.