Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions Orange/widgets/data/owcreateclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

import numpy as np

from AnyQt.QtWidgets import QGridLayout, QLabel, QLineEdit, QSizePolicy, QWidget
from AnyQt.QtCore import Qt
from AnyQt.QtWidgets import QFrame, QGridLayout, QLabel, QLineEdit, \
QSizePolicy, QWidget, QScrollArea
from AnyQt.QtCore import Qt, QTimer

from Orange.data import StringVariable, DiscreteVariable, Domain
from Orange.data.table import Table
Expand Down Expand Up @@ -227,6 +228,9 @@ class Outputs:

want_main_area = False
buttons_area_orientation = Qt.Vertical
#: Max pixel height of the rules area before it scrolls instead of

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is incomplete.

#: growing the widget further
MAX_RULES_AREA_HEIGHT = 360

settingsHandler = DomainContextHandler()
attribute = ContextSetting(None)
Expand Down Expand Up @@ -269,6 +273,9 @@ def __init__(self):
self.remove_buttons = []
#: list of list of QLabel: pairs of labels with counts
self.counts = []
#: bool: set by add_row, tells _refit_rules_area to scroll down
# once the new row's height has been applied
self._scroll_to_bottom_pending = False

gui.lineEdit(
self.controlArea, self, "class_name",
Expand Down Expand Up @@ -301,9 +308,15 @@ def __init__(self):
rules_box.addWidget(QLabel("Count"), 0, 3, 1, 2)
self.update_rules()

widg = QWidget(patternbox)
self._rules_widget = widg = QWidget()
widg.setLayout(rules_box)
patternbox.layout().addWidget(widg)

self._rules_scroll = scroll = QScrollArea()
scroll.setWidget(widg)
scroll.setWidgetResizable(True)
scroll.setFrameShape(QFrame.NoFrame)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
patternbox.layout().addWidget(scroll)

box = gui.hBox(patternbox)
gui.rubber(box)
Expand All @@ -328,7 +341,6 @@ def __init__(self):

gui.button(self.buttonsArea, self, "Apply", callback=self.apply)

# TODO: Resizing upon changing the number of rules does not work
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the extra blank lines.

@property
Expand Down Expand Up @@ -427,9 +439,27 @@ def _fix_tab_order():
_remove_line()
_fix_tab_order()

QTimer.singleShot(0, self._refit_rules_area)

def _refit_rules_area(self):
content_height = self._rules_widget.sizeHint().height()
self._rules_scroll.setFixedHeight(
min(content_height, self.MAX_RULES_AREA_HEIGHT))
self.adjustSize()

if self._scroll_to_bottom_pending:
self._scroll_to_bottom_pending = False
QTimer.singleShot(0, self._scroll_rules_to_bottom)

def _scroll_rules_to_bottom(self):
"""Scroll the rules area down so a newly added row is visible."""
bar = self._rules_scroll.verticalScrollBar()
bar.setValue(bar.maximum())

def add_row(self):
"""Append a new row at the end."""
self.active_rules.append(["", ""])
self._scroll_to_bottom_pending = True
self.adjust_n_rule_rows()
self.update_counts()

Expand Down
33 changes: 31 additions & 2 deletions Orange/widgets/visualize/owdistributions.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,16 @@ def update_setters(self):
self.initial_settings = {
self.LABELS_BOX: {
self.FONT_FAMILY_LABEL: self.FONT_FAMILY_SETTING,
self.TITLE_LABEL: self.FONT_SETTING,
self.AXIS_TITLE_LABEL: self.FONT_SETTING,
self.AXIS_TICKS_LABEL: self.FONT_SETTING,
self.LEGEND_LABEL: self.FONT_SETTING,
},
self.ANNOT_BOX: {
self.TITLE_LABEL: {self.TITLE_LABEL: ("", "")},
self.X_AXIS_LABEL: {self.TITLE_LABEL: ("", "")},
self.Y_AXIS_LABEL: {self.TITLE_LABEL: ("", "")},
},
self.PLOT_BOX: {
self.LEGEND_LABEL: {
self.HIDE_EMPTY_LABEL: (None, False)
Expand All @@ -209,6 +215,14 @@ def update_show_empty(**_settings):
self.LEGEND_LABEL: update_show_empty,
}

@property
def title_item(self):
return self.master.parent_widget.ploti.titleLabel

@property
def getAxis(self):
return self.master.parent_widget.ploti.getAxis

@property
def axis_items(self):
return [value["item"] for value in
Expand Down Expand Up @@ -648,17 +662,27 @@ def _clear_plot(self):
def _set_axis_names(self):
assert self.is_valid # called only from replot, so assumes data is OK
bottomaxis = self.ploti.getAxis("bottom")
bottomaxis.setLabel(self.var and self.var.name)
bottomaxis.setLabel(
self._custom_axis_title(ParameterSetter.X_AXIS_LABEL)
or (self.var and self.var.name))
bottomaxis.setShowUnit(not (self.var and self.var.is_time))

leftaxis = self.ploti.getAxis("left")
if self.show_probs and self.cvar:
custom_label = self._custom_axis_title(ParameterSetter.Y_AXIS_LABEL)
if custom_label:
leftaxis.setLabel(custom_label)
elif self.show_probs and self.cvar:
leftaxis.setLabel(
f"Probability of '{self.cvar.name}' at given '{self.var.name}'")
else:
leftaxis.setLabel("Frequency")
leftaxis.resizeEvent()

def _custom_axis_title(self, axis_label):
return self.visual_settings.get(
(ParameterSetter.ANNOT_BOX, axis_label, ParameterSetter.TITLE_LABEL),
"")

def _update_controls_state(self):
assert self.is_valid # called only from replot, so assumes data is OK
self.controls.sort_by_freq.setDisabled(self.var.is_continuous)
Expand Down Expand Up @@ -1391,6 +1415,11 @@ def send_report(self):
def set_visual_settings(self, key: KeyType, value: ValueType):
self.visual_settings[key] = value
self.plotview.parameter_setter.set_parameter(key, value)
if self.is_valid and key[:2] in (
(ParameterSetter.ANNOT_BOX, ParameterSetter.X_AXIS_LABEL),
(ParameterSetter.ANNOT_BOX, ParameterSetter.Y_AXIS_LABEL)):
# an empty custom title falls back to the auto-generated label
self._set_axis_names()


if __name__ == "__main__": # pragma: no cover
Expand Down
105 changes: 105 additions & 0 deletions Orange/widgets/visualize/tests/test_owdistributions.py
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,13 @@ def test_visual_settings(self):
key, value = ("Fonts", "Font family", "Font family"), "Helvetica"
self.widget.set_visual_settings(key, value)

key, value = ("Fonts", "Title", "Font size"), 20
self.widget.set_visual_settings(key, value)
key, value = ("Fonts", "Title", "Italic"), True
self.widget.set_visual_settings(key, value)
font.setPointSize(20)
self.assertFontEqual(graph.parameter_setter.title_item.item.font(), font)

key, value = ("Fonts", "Axis title", "Font size"), 16
self.widget.set_visual_settings(key, value)
key, value = ("Fonts", "Axis title", "Italic"), True
Expand Down Expand Up @@ -785,6 +792,104 @@ def test_visual_settings(self):
["Iris-versicolor", "Iris-virginica"]
)

self.assertFalse(graph.parameter_setter.title_item.isVisible())
key, value = ("Annotations", "Title", "Title"), "iris distributions"
self.widget.set_visual_settings(key, value)
self.assertTrue(graph.parameter_setter.title_item.isVisible())
self.assertEqual(graph.parameter_setter.title_item.item.toPlainText(), "iris distributions")

key, value = ("Annotations", "x-axis title", "Title"), "x-axis custom label"
self.widget.set_visual_settings(key, value)
self.assertEqual(self.widget.ploti.getAxis("bottom").labelText, "x-axis custom label")

key, value = ("Annotations", "y-axis title", "Title"), "y-axis custom label"
self.widget.set_visual_settings(key, value)
self.assertEqual(self.widget.ploti.getAxis("left").labelText, "y-axis custom label")

def test_custom_titles_variable_change(self):
"""Custom titles persist when plotted variable is changed"""
self.send_signal(self.widget.Inputs.data, self.iris)
graph = self.widget.plotview

key, value = ("Annotations", "Title", "Title"), "iris distributions"
self.widget.set_visual_settings(key, value)
key, value = ("Annotations", "x-axis title", "Title"), "x-axis custom label"
self.widget.set_visual_settings(key, value)
key, value = ("Annotations", "y-axis title", "Title"), "y-axis custom label"
self.widget.set_visual_settings(key, value)

self._set_var(1)

self.assertEqual(graph.parameter_setter.title_item.item.toPlainText(), "iris distributions")
self.assertEqual(self.widget.ploti.getAxis("bottom").labelText, "x-axis custom label")
self.assertEqual(self.widget.ploti.getAxis("left").labelText, "y-axis custom label")

key, value = ("Annotations", "x-axis title", "Title"), ""
self.widget.set_visual_settings(key, value)
self.assertEqual(self.widget.ploti.getAxis("bottom").labelText, "sepal length")

key, value = ("Annotations", "y-axis title", "Title"), ""
self.widget.set_visual_settings(key, value)
self.assertEqual(self.widget.ploti.getAxis("left").labelText, "Frequency")

def test_saved_workflow(self):
"""Visual settings are saved and restored"""
font = QFont()
font.setItalic(True)
font.setFamily("Helvetica")

key, value = ("Fonts", "Title", "Font size"), 20
self.widget.set_visual_settings(key, value)
key, value = ("Fonts", "Title", "Italic"), True
self.widget.set_visual_settings(key, value)
key, value = ("Fonts", "Axis title", "Font size"), 16
self.widget.set_visual_settings(key, value)
key, value = ("Fonts", "Axis title", "Italic"), True
self.widget.set_visual_settings(key, value)
key, value = ("Fonts", "Axis ticks", "Font size"), 15
self.widget.set_visual_settings(key, value)
key, value = ("Fonts", "Axis ticks", "Italic"), True
self.widget.set_visual_settings(key, value)
key, value = ("Fonts", "Legend", "Font size"), 14
self.widget.set_visual_settings(key, value)
key, value = ("Fonts", "Legend", "Italic"), True
self.widget.set_visual_settings(key, value)
key, value = ("Figure", "Legend", "Hide empty categories in the legend"), True
self.widget.set_visual_settings(key, value)
key, value = ("Annotations", "Title", "Title"), "iris distributions"
self.widget.set_visual_settings(key, value)
key, value = ("Annotations", "x-axis title", "Title"), "x-axis custom label"
self.widget.set_visual_settings(key, value)
key, value = ("Annotations", "y-axis title", "Title"), "y-axis custom label"
self.widget.set_visual_settings(key, value)

settings = self.widget.settingsHandler.pack_data(self.widget)
w = self.create_widget(OWDistributions, stored_settings=settings)

self.send_signal(w.Inputs.data, self.iris[50:], widget=w)
key, value = ("Fonts", "Font family", "Font family"), "Helvetica"
w.set_visual_settings(key, value)

font.setPointSize(20)
self.assertFontEqual(w.plotview.parameter_setter.title_item.item.font(), font)
font.setPointSize(16)
for item in w.plotview.parameter_setter.axis_items:
self.assertFontEqual(item.label.font(), font)
font.setPointSize(15)
for item in w.plotview.parameter_setter.axis_items:
self.assertFontEqual(item.style["tickFont"], font)
font.setPointSize(14)
legend_item = list(w.plotview.parameter_setter.legend_items)[0]
self.assertFontEqual(legend_item[1].item.font(), font)
self.assertEqual(
[i[1].text for i in w.plotview.parameter_setter.legend_items],
["Iris-versicolor", "Iris-virginica"]
)
self.assertTrue(w.plotview.parameter_setter.title_item.isVisible())
self.assertEqual(w.plotview.parameter_setter.title_item.item.toPlainText(), "iris distributions")
self.assertEqual(w.ploti.getAxis("bottom").labelText, "x-axis custom label")
self.assertEqual(w.ploti.getAxis("left").labelText, "y-axis custom label")

def assertFontEqual(self, font1, font2):
self.assertEqual(font1.family(), font2.family())
self.assertEqual(font1.pointSize(), font2.pointSize())
Expand Down
Loading