From eff594407efbec9088f8d70eb1125aaecf91deeb Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 14 Oct 2021 15:57:12 +0200 Subject: [PATCH 01/20] utils/dendrogram: Use palette colors --- Orange/widgets/utils/dendrogram.py | 55 +++++++++++------ Orange/widgets/utils/tests/test_dendrogram.py | 59 ++++++++++++++----- 2 files changed, 83 insertions(+), 31 deletions(-) diff --git a/Orange/widgets/utils/dendrogram.py b/Orange/widgets/utils/dendrogram.py index c112f99243b..d7ddca99142 100644 --- a/Orange/widgets/utils/dendrogram.py +++ b/Orange/widgets/utils/dendrogram.py @@ -7,8 +7,8 @@ from AnyQt.QtCore import QPointF, QRectF, Qt, QSizeF, QEvent, Signal from AnyQt.QtGui import ( - QPainterPath, QPen, QBrush, QPainterPathStroker, QColor, QTransform, - QFontMetrics, QPolygonF + QPainterPath, QPen, QBrush, QPalette, QPainterPathStroker, QColor, + QTransform, QFontMetrics, QPolygonF ) from AnyQt.QtWidgets import ( QGraphicsWidget, QGraphicsPathItem, QGraphicsItemGroup, @@ -218,7 +218,6 @@ def set_path(self, path): def set_label(self, label): self.label.setText(label) - self.label.setBrush(Qt.blue) self._update_label_pos() def set_color(self, color): @@ -348,7 +347,8 @@ def setRoot(self, root): self.clear() self._root = root if root is not None: - pen = make_pen(Qt.blue, width=1, cosmetic=True, + foreground = self.palette().color(QPalette.Foreground) + pen = make_pen(foreground, width=1, cosmetic=True, join_style=Qt.MiterJoin) for node in postorder(root): item = DendrogramWidget.ClusterGraphicsItem(self._itemgroup) @@ -437,19 +437,23 @@ def _set_hover_item(self, item): if self._highlighted_item is item: return - def branches(item): - return [self._items[ch] for ch in item.node.branches] + def set_pen(item, pen): + def branches(item): + return [self._items[ch] for ch in item.node.branches] + for it in postorder(item, branches): + it.setPen(pen) if self._highlighted_item: - pen = make_pen(Qt.blue, width=1, cosmetic=True) - for it in postorder(self._highlighted_item, branches): - it.setPen(pen) + # Restore the previous item + highlight = self.palette().color(QPalette.Foreground) + set_pen(self._highlighted_item, + make_pen(highlight, width=1, cosmetic=True)) self._highlighted_item = item if item: - hpen = make_pen(Qt.blue, width=2, cosmetic=True) - for it in postorder(item, branches): - it.setPen(hpen) + hpen = make_pen(self.palette().color(QPalette.Highlight), + width=2, cosmetic=True) + set_pen(item, hpen) def leafItems(self): """Iterate over the dendrogram leaf items (:class:`QGraphicsItem`). @@ -564,6 +568,7 @@ def _add_selection(self, item): ppath = self._create_path(item, path) label = self._create_label(len(self._selection)) selection_item = self._SelectionItem(self, ppath, outline, label) + selection_item.label.setBrush(self.palette().color(QPalette.Link)) selection_item.setPos(self.contentsRect().topLeft()) self._selection[item] = selection_item @@ -755,7 +760,7 @@ def _rescale(self): self._selection_items = None self._update_selection_items() - def sizeHint(self, which: Qt.SizeHint, constraint=QSizeF()) -> QRectF: + def sizeHint(self, which: Qt.SizeHint, constraint=QSizeF()) -> QSizeF: # reimplemented fm = QFontMetrics(self.font()) spacing = fm.lineSpacing() @@ -825,12 +830,11 @@ def sceneEventFilter(self, obj, event): def changeEvent(self, event): # reimplemented super().changeEvent(event) - if event.type() == QEvent.FontChange: self.updateGeometry() - - # QEvent.ContentsRectChange is missing in PyQt4 <= 4.11.3 - if event.type() == 178: # QEvent.ContentsRectChange: + elif event.type() == QEvent.PaletteChange: + self._update_colors() + elif event.type() == QEvent.ContentsRectChange: self._rescale() def resizeEvent(self, event): @@ -844,3 +848,20 @@ def mousePressEvent(self, event): # A mouse press on an empty widget part if event.modifiers() == Qt.NoModifier and self._selection: self.set_selected_clusters([]) + + def _update_colors(self): + def set_color(item: DendrogramWidget.ClusterGraphicsItem, color: QColor): + def branches(item): + return [self._items[ch] for ch in item.node.branches] + for it in postorder(item, branches): + it.setPen(update_pen(it.pen(), brush=color)) + if self._root is not None: + foreground = self.palette().color(QPalette.Foreground) + item = self.item(self._root) + set_color(item, foreground) + highlight = self.palette().color(QPalette.Highlight) + if self._highlighted_item is not None: + set_color(self._highlighted_item, highlight) + accent = self.palette().color(QPalette.Link) + for item in self._selection.values(): + item.label.setBrush(accent) diff --git a/Orange/widgets/utils/tests/test_dendrogram.py b/Orange/widgets/utils/tests/test_dendrogram.py index 9b35c9b1096..a3f040c78a2 100644 --- a/Orange/widgets/utils/tests/test_dendrogram.py +++ b/Orange/widgets/utils/tests/test_dendrogram.py @@ -1,18 +1,37 @@ +# pylint: disable=all import numpy as np from AnyQt.QtCore import Qt, QPoint -from AnyQt.QtWidgets import QGraphicsScene +from AnyQt.QtGui import QPalette, QColor +from AnyQt.QtTest import QTest +from AnyQt.QtWidgets import QGraphicsScene, QGraphicsView +from orangewidget.tests.utils import mouseMove from orangewidget.tests.base import GuiTest from Orange.clustering import hierarchical from Orange.widgets.utils.dendrogram import DendrogramWidget +T = hierarchical.Tree +C = hierarchical.ClusterData +S = hierarchical.SingletonData + + +def t(h: float, left: T, right: T): + return T(C((left.value.first, right.value.last), h), (left, right)) + + +def leaf(r, index): + return T(S((r, r + 1), 0.0, index)) + + class TestDendrogramWidget(GuiTest): def setUp(self) -> None: super().setUp() self.scene = QGraphicsScene() + self.view = QGraphicsView(self.scene) + self.view.resize(300, 300) self.widget = DendrogramWidget() self.scene.addItem(self.widget) @@ -23,19 +42,6 @@ def tearDown(self) -> None: def test_widget(self): w = self.widget - - T = hierarchical.Tree - C = hierarchical.ClusterData - S = hierarchical.SingletonData - - def t(h: float, left: T, right: T): - return T(C((left.value.first, right.value.last), h), (left, right)) - - def leaf(r, index): - return T(S((r, r + 1), 0.0, index)) - - T = hierarchical.Tree - w.set_root(t(0.0, leaf(0, 0), leaf(1, 1))) w.resize(w.effectiveSizeHint(Qt.PreferredSize)) h = w.height_at(QPoint()) @@ -56,3 +62,28 @@ def leaf(r, index): self.assertEqual(w.pos_at_height(0).x(), w.rect().right()) self.assertEqual(w.pos_at_height(height).x(), w.rect().left()) + + view = self.view + view.grab() # ensure w is laid out + root = w.root() + rootitem = w.item(root) + r = view.mapFromScene(rootitem.sceneBoundingRect()).boundingRect() + # move/hover over the item + mouseMove(view.viewport(), r.center()) + self.assertEqual(w._highlighted_item, rootitem) + # click select + QTest.mouseClick(view.viewport(), Qt.LeftButton, Qt.NoModifier, r.center()) + self.assertTrue(w.isItemSelected(rootitem)) + p = r.topLeft() + QPoint(-3, -3) # just out of the item + mouseMove(view.viewport(), p) + self.assertEqual(w._highlighted_item, None) + + def test_update_palette(self): + w = self.widget + w.set_root(t(1.0, leaf(0, 0), leaf(1, 1))) + w.setSelectedClusters([w.root()]) + p = QPalette() + p.setColor(QPalette.All, QPalette.WindowText, QColor(Qt.red)) + w.setPalette(p) + item = w.item(w.root()) + self.assertEqual(item.pen().color(), p.color(QPalette.WindowText)) From e5d32352107a2836b321b99c4a22699c0604b3f2 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 14 Oct 2021 15:58:55 +0200 Subject: [PATCH 02/20] graphicstextlist: Use palette colors --- Orange/widgets/utils/graphicstextlist.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Orange/widgets/utils/graphicstextlist.py b/Orange/widgets/utils/graphicstextlist.py index da7352c9b3a..3711611a1bd 100644 --- a/Orange/widgets/utils/graphicstextlist.py +++ b/Orange/widgets/utils/graphicstextlist.py @@ -3,7 +3,7 @@ from typing import Optional, Union, Any, Iterable, List, Callable, cast from AnyQt.QtCore import Qt, QSizeF, QEvent, QMarginsF, QPointF -from AnyQt.QtGui import QFont, QFontMetrics, QFontInfo +from AnyQt.QtGui import QFont, QFontMetrics, QFontInfo, QPalette from AnyQt.QtWidgets import ( QGraphicsWidget, QSizePolicy, QGraphicsItemGroup, QGraphicsSimpleTextItem, QGraphicsItem, QGraphicsScene, QGraphicsSceneResizeEvent, QToolTip, @@ -240,7 +240,7 @@ def changeEvent(self, event): elif event.type() == QEvent.PaletteChange: palette = self.palette() - brush = palette.brush(palette.Text) + brush = palette.brush(QPalette.Text) for item in self.__textitems: item.setBrush(brush) super().changeEvent(event) @@ -250,8 +250,10 @@ def __setup(self) -> None: font = self.__effectiveFont if self.__autoScale else self.font() assert self.__group is None group = QGraphicsItemGroup() + brush = self.palette().brush(QPalette.Text) for text in self.__items: t = QGraphicsSimpleTextItem(group) + t.setBrush(brush) t.setFont(font) t.setText(text) t.setData(0, text) From 4d86528d4a98ee5cf75fed510d890501dd4dd5a9 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 14 Oct 2021 16:03:48 +0200 Subject: [PATCH 03/20] heatmap: Use palette colors --- Orange/widgets/visualize/utils/heatmap.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Orange/widgets/visualize/utils/heatmap.py b/Orange/widgets/visualize/utils/heatmap.py index 8212e841060..29c9e0069ca 100644 --- a/Orange/widgets/visualize/utils/heatmap.py +++ b/Orange/widgets/visualize/utils/heatmap.py @@ -405,6 +405,7 @@ def setHeatmaps(self, parts: 'Parts') -> None: for i, rowitem in enumerate(parts.rows): if rowitem.title: item = QGraphicsSimpleTextItem(rowitem.title, parent=self) + item.setBrush(self.palette().text()) item.setTransform(item.transform().rotate(-90)) item = SimpleLayoutItem(item, parent=grid, anchor=(0, 1), anchorItem=(0, 0)) @@ -433,10 +434,10 @@ def setHeatmaps(self, parts: 'Parts') -> None: for j, colitem in enumerate(parts.columns): if colitem.title: - item = SimpleLayoutItem( - QGraphicsSimpleTextItem(colitem.title, parent=self), - parent=grid, anchor=(0.5, 0.5), anchorItem=(0.5, 0.5) - ) + item = QGraphicsSimpleTextItem(colitem.title, parent=self) + item.setBrush(self.palette().text()) + item = SimpleLayoutItem(item, parent=grid, anchor=(0.5, 0.5), + anchorItem=(0.5, 0.5)) item.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) grid.addItem(item, self.GroupTitleRow, Col0 + 2 * j + 1) From 4cf9267ceba52780d0e9867ec521d73742788e57 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 14 Oct 2021 16:04:37 +0200 Subject: [PATCH 04/20] owtreeviewer: Use palette colors --- Orange/widgets/visualize/owtreeviewer.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Orange/widgets/visualize/owtreeviewer.py b/Orange/widgets/visualize/owtreeviewer.py index 2fea79c2a01..0aa176ed3f4 100644 --- a/Orange/widgets/visualize/owtreeviewer.py +++ b/Orange/widgets/visualize/owtreeviewer.py @@ -123,7 +123,7 @@ def boundingRect(self): QSizeF(self.attr_text_w, self.attr_text_h)) else: attr_rect = QRectF(0, 0, 1, 1) - rect = self.rect().adjusted(-5, -5, 5, 5) + rect = self.rect().adjusted(-6, -6, 6, 6) return rect | attr_rect def paint(self, painter, option, widget=None): @@ -142,7 +142,11 @@ def paint(self, painter, option, widget=None): painter.drawText(QPointF(x, -self.line_descent - 1), draw_text) painter.save() painter.setBrush(self.backgroundBrush) - painter.setPen(QPen(Qt.black, 3 if self.isSelected() else 0)) + if self.isSelected(): + outline = QPen(option.palette.highlight(), 3) + else: + outline = QPen(option.palette.dark(), 1) + painter.setPen(outline) adjrect = rect.adjusted(-3, 0, 0, 0) if not self.tree_adapter.has_children(self.node_inst): painter.drawRoundedRect(adjrect, 4, 4) From d635839722632d19f0153ff6f2aa92e4fee3ecd7 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 14 Oct 2021 16:07:32 +0200 Subject: [PATCH 05/20] widgets: Use palette colors --- Orange/widgets/data/owpaintdata.py | 7 +++--- Orange/widgets/data/owselectrows.py | 4 ++-- Orange/widgets/evaluate/owcalibrationplot.py | 8 ++++--- Orange/widgets/evaluate/owconfusionmatrix.py | 2 ++ Orange/widgets/evaluate/owliftcurve.py | 3 ++- Orange/widgets/evaluate/owrocanalysis.py | 22 ++++++++++++------- .../widgets/unsupervised/owcorrespondence.py | 5 +++-- Orange/widgets/unsupervised/owdistancemap.py | 5 +++-- Orange/widgets/utils/plot/owplotgui.py | 7 +++--- Orange/widgets/visualize/owfreeviz.py | 5 +++-- .../widgets/visualize/owlinearprojection.py | 5 +++-- Orange/widgets/visualize/owradviz.py | 6 ++--- .../widgets/visualize/owscatterplotgraph.py | 6 ++--- Orange/widgets/visualize/utils/component.py | 5 +++-- 14 files changed, 54 insertions(+), 36 deletions(-) diff --git a/Orange/widgets/data/owpaintdata.py b/Orange/widgets/data/owpaintdata.py index cc2f46be0ae..bf254897d5a 100644 --- a/Orange/widgets/data/owpaintdata.py +++ b/Orange/widgets/data/owpaintdata.py @@ -512,7 +512,8 @@ def mouseReleaseEvent(self, event): def activate(self): if self._item is None: - self._item = _RectROI((0, 0), (0, 0), pen=(25, 25, 25)) + pen = self._plot.palette().color(QPalette.Text) + self._item = _RectROI((0, 0), (0, 0), pen=pen) self._item.setAcceptedMouseButtons(Qt.LeftButton) self._item.setVisible(False) self._item.setCursor(Qt.OpenHandCursor) @@ -938,8 +939,8 @@ def _init_ui(self): # main area GUI viewbox = PaintViewBox(enableMouse=False) - self.plotview = pg.PlotWidget(background="w", viewBox=viewbox) - self.plotview.sizeHint = lambda: QSize(200, 100) # Minimum size for 1-d painting + self.plotview = pg.PlotWidget(background=None, viewBox=viewbox) + self.plotview.setBackgroundRole(QPalette.Base) self.plot = self.plotview.getPlotItem() axis_color = self.palette().color(QPalette.Text) diff --git a/Orange/widgets/data/owselectrows.py b/Orange/widgets/data/owselectrows.py index ed6a7d59898..ce65a459211 100644 --- a/Orange/widgets/data/owselectrows.py +++ b/Orange/widgets/data/owselectrows.py @@ -299,8 +299,8 @@ def add_row(self, attr=None, condition_type=None, condition_value=None): index = QPersistentModelIndex(model.index(row, 3)) temp_button = QPushButton('×', self, flat=True, - styleSheet='* {font-size: 16pt; color: silver}' - '*:hover {color: black}') + styleSheet='* {font-size: 16pt; color: palette(button-text) }' + '*:hover {color: palette(bright-text)}') temp_button.clicked.connect(lambda: self.remove_one(index.row())) self.cond_list.setCellWidget(row, 3, temp_button) diff --git a/Orange/widgets/evaluate/owcalibrationplot.py b/Orange/widgets/evaluate/owcalibrationplot.py index 7124d24a6af..885705b2cb9 100644 --- a/Orange/widgets/evaluate/owcalibrationplot.py +++ b/Orange/widgets/evaluate/owcalibrationplot.py @@ -3,6 +3,7 @@ import numpy as np from AnyQt.QtCore import Qt, QSize +from AnyQt.QtGui import QPalette, QPen from AnyQt.QtWidgets import QListWidget, QSizePolicy import pyqtgraph as pg @@ -191,17 +192,18 @@ def __init__(self): gui.auto_apply(self.buttonsArea, self, "auto_commit") - self.plotview = pg.GraphicsView(background="w") + self.plotview = pg.GraphicsView(background=None) + self.plotview.setBackgroundRole(QPalette.Base) axes = {"bottom": AxisItem(orientation="bottom"), "left": AxisItem(orientation="left")} self.plot = pg.PlotItem(enableMenu=False, axisItems=axes) self.plot.parameter_setter = ParameterSetter(self.plot) self.plot.setMouseEnabled(False, False) self.plot.hideButtons() - + pen = QPen(self.palette().color(QPalette.Text)) for axis_name in ("bottom", "left"): axis = self.plot.getAxis(axis_name) - axis.setPen(pg.mkPen(color=0.0)) + axis.setPen(pen) # Remove the condition (that is, allow setting this for bottom # axis) when pyqtgraph is fixed # Issue: https://github.com/pyqtgraph/pyqtgraph/issues/930 diff --git a/Orange/widgets/evaluate/owconfusionmatrix.py b/Orange/widgets/evaluate/owconfusionmatrix.py index 624d874f1dd..9560baaa066 100644 --- a/Orange/widgets/evaluate/owconfusionmatrix.py +++ b/Orange/widgets/evaluate/owconfusionmatrix.py @@ -483,6 +483,8 @@ def _isinvalid(x): [0, 240][i == j], 160, 255 if _isinvalid(col_val) else int(255 - 30 * col_val)) item.setData(QBrush(bkcolor), Qt.BackgroundRole) + # bkcolor is light-ish so use a black text + item.setData(QBrush(Qt.black), Qt.ForegroundRole) item.setData("trbl", BorderRole) item.setToolTip("actual: {}\npredicted: {}".format( self.headers[i], self.headers[j])) diff --git a/Orange/widgets/evaluate/owliftcurve.py b/Orange/widgets/evaluate/owliftcurve.py index e7d49f22d9b..bd70b9da618 100644 --- a/Orange/widgets/evaluate/owliftcurve.py +++ b/Orange/widgets/evaluate/owliftcurve.py @@ -214,7 +214,8 @@ def __init__(self): gui.rubber(self.controlArea) - self.plotview = pg.GraphicsView(background="w") + self.plotview = pg.GraphicsView(background=None) + self.plotview.setBackgroundRole(QPalette.Base) self.plotview.setFrameStyle(QFrame.StyledPanel) axes = {"bottom": AxisItem(orientation="bottom"), diff --git a/Orange/widgets/evaluate/owrocanalysis.py b/Orange/widgets/evaluate/owrocanalysis.py index 4aead7e4475..5daba08cc1f 100644 --- a/Orange/widgets/evaluate/owrocanalysis.py +++ b/Orange/widgets/evaluate/owrocanalysis.py @@ -395,9 +395,11 @@ def __init__(self): grid.addWidget(QLabel("Prior probability:")) grid.addWidget(self.target_prior_sp, 2, 1) - self.plotview = pg.GraphicsView(background="w") + self.plotview = pg.GraphicsView(background=None) + self.plotview.setBackgroundRole(QPalette.Base) self.plotview.setFrameStyle(QFrame.StyledPanel) self.plotview.scene().sigMouseMoved.connect(self._on_mouse_moved) + self.plotview.scene().setPalette(self.palette()) self.plot = pg.PlotItem(enableMenu=False) self.plot.setMouseEnabled(False, False) @@ -420,7 +422,7 @@ def __init__(self): axis.setLabel("TP Rate (Sensitivity)") axis.setGrid(16) - self.plot.showGrid(True, True, alpha=0.1) + self.plot.showGrid(True, True, alpha=0.2) self.plot.setRange(xRange=(0.0, 1.0), yRange=(0.0, 1.0), padding=0.05) self.plotview.setCentralItem(self.plot) @@ -553,6 +555,7 @@ def merge_averaging(): ind = np.argmin(np.abs(points.thresholds - 0.5)) item = pg.TextItem( text="{:.3f}".format(points.thresholds[ind]), + color=foreground ) item.setPos(points.fpr[ind], points.tpr[ind]) self.plot.addItem(item) @@ -560,7 +563,7 @@ def merge_averaging(): hull_curves = [curve.merged.hull for curve in selected] if hull_curves: self._rocch = convex_hull(hull_curves) - iso_pen = QPen(QColor(Qt.black), 1) + iso_pen = QPen(foreground, 1.0) iso_pen.setCosmetic(True) self._perf_line = InfiniteLine(pen=iso_pen, antialias=True) self.plot.addItem(self._perf_line) @@ -596,7 +599,7 @@ def no_averaging(): OWROCAnalysis.Threshold: threshold_averaging, OWROCAnalysis.NoAveraging: no_averaging } - + foreground = self.plotview.scene().palette().color(QPalette.Text) target = self.target_index selected = self.selected_classifiers @@ -606,16 +609,19 @@ def no_averaging(): if self.display_convex_hull and hull_curves: hull = convex_hull(hull_curves) - hull_pen = QPen(QColor(200, 200, 200, 100), 2) + hull_color = QColor(foreground) + hull_color.setAlpha(100) + hull_pen = QPen(hull_color, 2) hull_pen.setCosmetic(True) + hull_color.setAlpha(50) item = self.plot.plot( hull.fpr, hull.tpr, pen=hull_pen, - brush=QBrush(QColor(200, 200, 200, 50)), + brush=QBrush(hull_color), fillLevel=0) item.setZValue(-10000) - - pen = QPen(QColor(100, 100, 100, 100), 1, Qt.DashLine) + line_color = self.palette().color(QPalette.Disabled, QPalette.Text) + pen = QPen(QColor(*line_color.getRgb()[:3], 200), 1.0, Qt.DashLine) pen.setCosmetic(True) self.plot.plot([0, 1], [0, 1], pen=pen, antialias=True) diff --git a/Orange/widgets/unsupervised/owcorrespondence.py b/Orange/widgets/unsupervised/owcorrespondence.py index af9fd91689f..feba1a20194 100644 --- a/Orange/widgets/unsupervised/owcorrespondence.py +++ b/Orange/widgets/unsupervised/owcorrespondence.py @@ -4,7 +4,7 @@ import numpy as np from AnyQt.QtWidgets import QListView, QApplication, QSizePolicy -from AnyQt.QtGui import QBrush, QColor, QPainter +from AnyQt.QtGui import QBrush, QColor, QPainter, QPalette from AnyQt.QtCore import QEvent, Qt from orangewidget.utils.listview import ListViewSearch @@ -277,6 +277,7 @@ def get_minmax(points): margin = margin * 0.05 if margin > 1e-10 else 1 self.plot.setYRange(minmax[2] - margin, minmax[3] + margin) + foreground = self.palette().color(QPalette.Text) for i, (v, points) in enumerate(zip(variables, points)): color_outline = colors[i] color_outline.setAlpha(200) @@ -290,7 +291,7 @@ def get_minmax(points): self.plot.addItem(item) for name, point in zip(v.values, points): - item = pg.TextItem(name, anchor=(0.5, 0)) + item = pg.TextItem(name, anchor=(0.5, 0), color=foreground) self.plot.addItem(item) item.setPos(point[0], point[1]) diff --git a/Orange/widgets/unsupervised/owdistancemap.py b/Orange/widgets/unsupervised/owdistancemap.py index 9543b0d84e4..d7d86d08541 100644 --- a/Orange/widgets/unsupervised/owdistancemap.py +++ b/Orange/widgets/unsupervised/owdistancemap.py @@ -7,7 +7,7 @@ from AnyQt.QtWidgets import ( QGraphicsRectItem, QGraphicsGridLayout, QApplication, QSizePolicy ) -from AnyQt.QtGui import QFontMetrics, QPen, QTransform, QFont +from AnyQt.QtGui import QFontMetrics, QPen, QTransform, QFont, QPalette from AnyQt.QtCore import Qt, QRect, QRectF, QPointF from AnyQt.QtCore import pyqtSignal as Signal @@ -344,7 +344,8 @@ def _set_thresholds(low, high): gui.auto_send(self.buttonsArea, self, "autocommit") - self.view = GraphicsView(background="w") + self.view = GraphicsView(background=None) + self.view.setBackgroundRole(QPalette.Base) self.mainArea.layout().addWidget(self.view) self.grid_widget = pg.GraphicsWidget() diff --git a/Orange/widgets/utils/plot/owplotgui.py b/Orange/widgets/utils/plot/owplotgui.py index a2f040a67fb..f91d922d1d0 100644 --- a/Orange/widgets/utils/plot/owplotgui.py +++ b/Orange/widgets/utils/plot/owplotgui.py @@ -31,7 +31,7 @@ QWidget, QToolButton, QVBoxLayout, QHBoxLayout, QGridLayout, QMenu, QAction, QSizePolicy, QLabel, QStyledItemDelegate, QStyle, QListView ) -from AnyQt.QtGui import QIcon, QColor, QFont +from AnyQt.QtGui import QIcon, QFont, QPalette from AnyQt.QtCore import Qt, pyqtSignal, QSize, QRect, QPoint, QMimeData from Orange.data import ContinuousVariable, DiscreteVariable @@ -145,12 +145,13 @@ def paint(self, painter, option, index): txtw = painter.fontMetrics().horizontalAdvance(txt) painter.save() painter.setPen(Qt.NoPen) - painter.setBrush(QColor("#ccc")) + painter.setBrush(option.palette.brush(QPalette.Button)) brect = QRect(rect.x() + rect.width() - 8 - txtw, rect.y(), txtw, rect.height()) painter.drawRoundedRect(brect, 4, 4) - painter.restore() + painter.setPen(option.palette.color(QPalette.ButtonText)) painter.drawText(brect, Qt.AlignCenter, txt) + painter.restore() painter.save() double_pen = painter.pen() diff --git a/Orange/widgets/visualize/owfreeviz.py b/Orange/widgets/visualize/owfreeviz.py index a6fc83c489a..6db04a29f4e 100644 --- a/Orange/widgets/visualize/owfreeviz.py +++ b/Orange/widgets/visualize/owfreeviz.py @@ -5,7 +5,7 @@ import numpy as np from AnyQt.QtCore import Qt, QRectF, QLineF, QPoint -from AnyQt.QtGui import QColor +from AnyQt.QtGui import QPalette import pyqtgraph as pg @@ -105,7 +105,8 @@ def update_circle(self): if self.circle_item is not None: r = self.scaled_radius self.circle_item.setRect(QRectF(-r, -r, 2 * r, 2 * r)) - pen = pg.mkPen(QColor(Qt.lightGray), width=1, cosmetic=True) + color = self.plot_widget.palette().color(QPalette.Disabled, QPalette.Text) + pen = pg.mkPen(color, width=1, cosmetic=True) self.circle_item.setPen(pen) def _add_indicator_item(self, anchor_idx): diff --git a/Orange/widgets/visualize/owlinearprojection.py b/Orange/widgets/visualize/owlinearprojection.py index ab818039653..48fcdf888af 100644 --- a/Orange/widgets/visualize/owlinearprojection.py +++ b/Orange/widgets/visualize/owlinearprojection.py @@ -11,7 +11,7 @@ from sklearn.neighbors import NearestNeighbors from sklearn.metrics import r2_score -from AnyQt.QtGui import QStandardItem, QColor +from AnyQt.QtGui import QStandardItem, QPalette from AnyQt.QtCore import Qt, QRectF, QLineF, pyqtSignal as Signal import pyqtgraph as pg @@ -253,7 +253,8 @@ def update_circle(self): r = self.scaled_radius * np.max(np.linalg.norm(points, axis=1)) self.circle_item.setRect(QRectF(-r, -r, 2 * r, 2 * r)) - pen = pg.mkPen(QColor(Qt.lightGray), width=1, cosmetic=True) + color = self.plot_widget.palette().color(QPalette.Disabled, QPalette.Text) + pen = pg.mkPen(color, width=1, cosmetic=True) self.circle_item.setPen(pen) diff --git a/Orange/widgets/visualize/owradviz.py b/Orange/widgets/visualize/owradviz.py index 8272d749ae4..781723c6ba7 100644 --- a/Orange/widgets/visualize/owradviz.py +++ b/Orange/widgets/visualize/owradviz.py @@ -7,7 +7,7 @@ from sklearn.model_selection import cross_val_score from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor -from AnyQt.QtGui import QStandardItem, QColor +from AnyQt.QtGui import QStandardItem, QColor, QPalette from AnyQt.QtCore import Qt, QRectF, QPoint, pyqtSignal as Signal import pyqtgraph as pg @@ -244,8 +244,9 @@ def update_anchors(self): self.anchor_items = [] label_len = 1 + foreground = self.plot_widget.palette().color(QPalette.Text) for point, label in zip(points, labels): - anchor = TextItem() + anchor = TextItem(color=foreground) anchor.textItem.setToolTip(f"{label}") if len(label) > MAX_LABEL_LEN: @@ -266,7 +267,6 @@ def update_anchors(self): anchor.setText(label) anchor.setFont(self.parameter_setter.anchor_font) label_len = min(MAX_LABEL_LEN, len(label)) - anchor.setColor(QColor(0, 0, 0)) x, y = point angle = np.rad2deg(np.arctan2(y, x)) diff --git a/Orange/widgets/visualize/owscatterplotgraph.py b/Orange/widgets/visualize/owscatterplotgraph.py index 2b5c2426cb2..03884760ed7 100644 --- a/Orange/widgets/visualize/owscatterplotgraph.py +++ b/Orange/widgets/visualize/owscatterplotgraph.py @@ -9,7 +9,7 @@ from AnyQt.QtCore import Qt, QRectF, QSize, QTimer, pyqtSignal as Signal, \ QObject from AnyQt.QtGui import QColor, QPen, QBrush, QPainterPath, QTransform, \ - QPainter + QPainter, QPalette from AnyQt.QtWidgets import QApplication, QToolTip, QGraphicsTextItem, \ QGraphicsRectItem, QGraphicsItemGroup @@ -1290,12 +1290,12 @@ def update_labels(self): if self._too_many_labels or mask is None or not np.any(mask): return - black = pg.mkColor(0, 0, 0) + foreground = self.plot_widget.palette().color(QPalette.Text) labels = labels[mask] x = x[mask] y = y[mask] for label, xp, yp in zip(labels, x, y): - ti = TextItem(label, black) + ti = TextItem(label, foreground) ti.setPos(xp, yp) self.plot_widget.addItem(ti) self.labels.append(ti) diff --git a/Orange/widgets/visualize/utils/component.py b/Orange/widgets/visualize/utils/component.py index da54482a2fc..8a1a1c2216e 100644 --- a/Orange/widgets/visualize/utils/component.py +++ b/Orange/widgets/visualize/utils/component.py @@ -1,7 +1,7 @@ """Common gui.OWComponent components.""" from AnyQt.QtCore import Qt, QRectF -from AnyQt.QtGui import QColor, QFont +from AnyQt.QtGui import QFont, QPalette from AnyQt.QtWidgets import QGraphicsEllipseItem import pyqtgraph as pg @@ -71,7 +71,8 @@ def update_circle(self): if self.scatterplot_item is not None and not self.circle_item: self.circle_item = QGraphicsEllipseItem() self.circle_item.setRect(QRectF(-1, -1, 2, 2)) - self.circle_item.setPen(pg.mkPen(QColor(0, 0, 0), width=2)) + color = self.plot_widget.palette().color(QPalette.Text) + self.circle_item.setPen(pg.mkPen(color, width=2)) self.plot_widget.addItem(self.circle_item) def reset_button_clicked(self): From 659d4c0185c1e9d9a22508f319d1b45581460cbb Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 14 Oct 2021 15:53:07 +0200 Subject: [PATCH 06/20] owvenndiagram: Use palette colors --- Orange/widgets/visualize/owvenndiagram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Orange/widgets/visualize/owvenndiagram.py b/Orange/widgets/visualize/owvenndiagram.py index d7d21848fef..194223159a3 100644 --- a/Orange/widgets/visualize/owvenndiagram.py +++ b/Orange/widgets/visualize/owvenndiagram.py @@ -983,11 +983,11 @@ def setItems(self, items): font = self.font() font.setPixelSize(14) - + palette = self.palette() for item in items: text = GraphicsTextEdit(self) text.setFont(font) - text.setDefaultTextColor(QColor("#333")) + text.setDefaultTextColor(palette.color(QPalette.Text)) text.setHtml(fmt(escape(item.text), item.informativeText)) text.adjustSize() text.editingStarted.connect(self._on_editingStarted) From 51a09efc22e24c7a4f3818637649327952b7ad26 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 14 Oct 2021 16:08:03 +0200 Subject: [PATCH 07/20] plotutils: Add pg.PlotWidget pg.AxisItem that are palette aware --- .../widgets/visualize/owscatterplotgraph.py | 8 +- Orange/widgets/visualize/utils/plotutils.py | 92 ++++++++++++++++++- 2 files changed, 94 insertions(+), 6 deletions(-) diff --git a/Orange/widgets/visualize/owscatterplotgraph.py b/Orange/widgets/visualize/owscatterplotgraph.py index 03884760ed7..98b0b0f54b9 100644 --- a/Orange/widgets/visualize/owscatterplotgraph.py +++ b/Orange/widgets/visualize/owscatterplotgraph.py @@ -27,7 +27,7 @@ CommonParameterSetter from Orange.widgets.visualize.utils.plotutils import ( HelpEventDelegate as EventDelegate, InteractiveViewBox as ViewBox, - PaletteItemSample, SymbolItemSample, AxisItem + PaletteItemSample, SymbolItemSample, AxisItem, PlotWidget ) SELECTION_WIDTH = 5 @@ -546,8 +546,10 @@ def __init__(self, scatter_widget, parent=None, view_box=ViewBox): self.view_box = view_box(self) _axis = {"left": AxisItem("left"), "bottom": AxisItem("bottom")} - self.plot_widget = pg.PlotWidget(viewBox=self.view_box, parent=parent, - background="w", axisItems=_axis) + self.plot_widget = PlotWidget( + viewBox=self.view_box, parent=parent, background=None, + axisItems=_axis + ) self.plot_widget.hideAxis("left") self.plot_widget.hideAxis("bottom") self.plot_widget.getPlotItem().buttonsHidden = True diff --git a/Orange/widgets/visualize/utils/plotutils.py b/Orange/widgets/visualize/utils/plotutils.py index ef79028c7f6..8a0a2395b6d 100644 --- a/Orange/widgets/visualize/utils/plotutils.py +++ b/Orange/widgets/visualize/utils/plotutils.py @@ -6,10 +6,11 @@ QRectF, QLineF, QObject, QEvent, Qt, pyqtSignal as Signal ) from AnyQt.QtGui import QTransform, QFontMetrics, QStaticText, QBrush, QPen, \ - QFont + QFont, QPalette from AnyQt.QtWidgets import ( QGraphicsLineItem, QGraphicsSceneMouseEvent, QPinchGesture, - QGraphicsItemGroup, QWidget) + QGraphicsItemGroup, QWidget +) import pyqtgraph as pg import pyqtgraph.functions as fn @@ -497,7 +498,57 @@ def paint(self, p, *args): drawSymbol(p, self.__symbol, self.__size, self.__pen, self.__brush) -class AxisItem(pg.AxisItem): +class StyledAxisItem(pg.AxisItem): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.label.setDefaultTextColor(self.palette().color(QPalette.Text)) + + def changeEvent(self, event: QEvent) -> None: + if event.type() == QEvent.FontChange: + self.picture = None + self.update() + elif event.type() == QEvent.PaletteChange: + self.picture = None + self.label.setDefaultTextColor(self.palette().color(QPalette.Text)) + self.update() + super().changeEvent(event) + + __hasTextPen = False + + def setTextPen(self, *args, **kwargs): + self.__hasTextPen = args or kwargs + super().setTextPen(*args, **kwargs) + if not self.__hasTextPen: + self.__clear_labelStyle_color() + + def textPen(self): + if self.__hasTextPen: + return super().textPen() + else: # bypass pg.AxisItem + return QPen(self.palette().brush(QPalette.Text), 1) + + __hasPen = False + + def setPen(self, *args, **kwargs): + self.__hasPen = bool(args or kwargs) + super().setPen(*args, **kwargs) + if not self.__hasPen: + self.__clear_labelStyle_color() + + def pen(self): + if self.__hasPen: + return super().pen() + else: # bypass pg.AxisItem + return QPen(self.palette().brush(QPalette.Text), 1) + + def __clear_labelStyle_color(self): + try: + self.labelStyle.pop("color") + except AttributeError: + pass + + +class AxisItem(StyledAxisItem): def __init__(self, orientation, rotate_ticks=False, **kwargs): super().__init__(orientation, **kwargs) self.style["rotateTicks"] = rotate_ticks @@ -543,3 +594,38 @@ def drawPicture(self, p, axisSpec, tickSpecs, textSpecs): self._updateMaxTextSize(max_text_size + offset) else: super().drawPicture(p, axisSpec, tickSpecs, textSpecs) + + +class PlotWidget(pg.PlotWidget): + """ + A pyqtgraph.PlotWidget with better QPalette integration. + + A default constructed plot will respect and adapt to the current palette + """ + def __init__(self, *args, background=None, **kwargs): + axisItems = kwargs.pop("axisItems", None) + if axisItems is None: # Use palette aware AxisItems + axisItems = {"left": AxisItem("left"), "bottom": AxisItem("bottom")} + super().__init__(*args, background=background, axisItems=axisItems, + **kwargs) + if background is None: + # Revert the pg.PlotWidget's modifications, use default + # for QGraphicsView background role + self.setBackgroundRole(QPalette.Base) + self.__updateScenePalette() + + def setScene(self, scene): + super().setScene(scene) + self.__updateScenePalette() + + def changeEvent(self, event): + if event.type() == QEvent.PaletteChange and \ + self.scene() is not None and self.scene().parent() is self: + self.__updateScenePalette() + self.resetCachedContent() + super().changeEvent(event) + + def __updateScenePalette(self): + scene = self.scene() + if scene is not None and scene.palette() != self.palette(): + scene.setPalette(self.palette()) From a99e732dec84b1c468bc37a12dda535da4ee7bbd Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 5 Oct 2017 15:46:12 +0200 Subject: [PATCH 08/20] owsilhouetteplot: Use palette for GUI element colors --- Orange/widgets/visualize/owsilhouetteplot.py | 59 ++++++++++++++------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/Orange/widgets/visualize/owsilhouetteplot.py b/Orange/widgets/visualize/owsilhouetteplot.py index 4e7dd04bc62..19513e44e4f 100644 --- a/Orange/widgets/visualize/owsilhouetteplot.py +++ b/Orange/widgets/visualize/owsilhouetteplot.py @@ -22,8 +22,6 @@ ) from AnyQt.QtCore import pyqtSignal as Signal -import pyqtgraph as pg - import Orange.data from Orange.data.util import get_unique_names import Orange.distance @@ -34,13 +32,14 @@ from Orange.widgets import widget, gui, settings from Orange.widgets.utils.graphicsscene import GraphicsScene from Orange.widgets.utils.stickygraphicsview import StickyGraphicsView -from Orange.widgets.utils import itemmodels +from Orange.widgets.utils import itemmodels, apply_all from Orange.widgets.utils.annotated_data import (create_annotated_table, ANNOTATED_DATA_SIGNAL_NAME) from Orange.widgets.utils.graphicstextlist import TextListWidget from Orange.widgets.utils.graphicslayoutitem import SimpleLayoutItem from Orange.widgets.utils.sql import check_sql_input from Orange.widgets.utils.widgetpreview import WidgetPreview +from Orange.widgets.visualize.utils.plotutils import AxisItem from Orange.widgets.widget import Msg, Input, Output @@ -180,7 +179,7 @@ def __init__(self): gui.auto_send(self.buttonsArea, self, "auto_commit") self.scene = GraphicsScene(self) - self.view = StickyGraphicsView(self.scene) + self.view = StyledGraphicsView(self.scene) self.view.setRenderHint(QPainter.Antialiasing, True) self.view.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.mainArea.layout().addWidget(self.view) @@ -570,6 +569,28 @@ def helpEvent(self, event: QGraphicsSceneHelpEvent): return +class StyledGraphicsView(StickyGraphicsView): + """ + Propagate style and palette changes to the visualized scene. + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ensurePolished() + if self.scene() is not None: + self.scene().setPalette(self.palette()) + + def setScene(self, scene): + super().setScene(scene) + if self.scene() is not None: + self.scene().setPalette(self.palette()) + + def changeEvent(self, event): + if event.type() == QEvent.PaletteChange and \ + self.scene() is not None and self.scene().parent() is self: + self.scene().setPalette(self.palette()) + super().changeEvent(event) + + class SilhouettePlot(QGraphicsWidget): """ A silhouette plot widget. @@ -589,8 +610,8 @@ def __init__(self, parent=None, **kwargs): self.__pen = QPen(Qt.NoPen) self.__layout = QGraphicsGridLayout() self.__hoveredItem = None - self.__topScale = None # type: Optional[pg.AxisItem] - self.__bottomScale = None # type: Optional[pg.AxisItem] + self.__topScale = None # type: Optional[AxisItem] + self.__bottomScale = None # type: Optional[AxisItem] self.__layout.setColumnSpacing(0, 1.) self.setLayout(self.__layout) self.setFocusPolicy(Qt.StrongFocus) @@ -721,10 +742,8 @@ def __setup(self): font = self.font() font.setPixelSize(self.__barHeight) - axispen = QPen(Qt.black) - - ax = pg.AxisItem(parent=self, orientation="top", maxTickLength=7, - pen=axispen) + foreground = self.palette().brush(QPalette.Foreground) + ax = AxisItem(parent=self, orientation="top", maxTickLength=7) ax.setRange(smin, smax) self.__topScale = ax layout = self.__layout @@ -746,6 +765,8 @@ def __setup(self): label = QGraphicsSimpleTextItem( "{} ({})".format(group.label, len(group.scores)), self ) + label.setBrush(foreground) + label.setPen(QPen(Qt.NoPen)) label.setRotation(-90) item = SimpleLayoutItem( label, @@ -772,8 +793,7 @@ def __setup(self): layout.addItem(textlist, i + 1, 3) - ax = pg.AxisItem(parent=self, orientation="bottom", maxTickLength=7, - pen=axispen) + ax = AxisItem(parent=self, orientation="bottom", maxTickLength=7) ax.setRange(smin, smax) self.__bottomScale = ax layout.addItem(ax, len(self.__groups) + 1, 2) @@ -800,6 +820,14 @@ def __updateSizeConstraints(self): axis.setMaximumWidth(mwidth) axis.setMinimumWidth(mwidth) + def changeEvent(self, event: QEvent) -> None: + if event.type() == QEvent.PaletteChange: + brush = self.palette().brush(QPalette.Text) + labels = [it for it in self.childItems() + if isinstance(it, QGraphicsSimpleTextItem)] + apply_all(labels, lambda it: it.setBrush(brush)) + super().changeEvent(event) + def event(self, event: QEvent) -> bool: # Reimplemented if event.type() == QEvent.LayoutRequest and \ @@ -1122,11 +1150,8 @@ def sizeHint(self, which, constraint=QRectF()): def paint(self, painter, option, widget=None): # type: (QPainter, QStyleOptionGraphicsItem, Optional[QWidget]) -> None - palette = option.palette # type: QPalette - role = QPalette.WindowText - if widget is not None: - role = widget.foregroundRole() - color = palette.color(role) + palette = self.palette() # type: QPalette + color = palette.color(QPalette.Foreground) painter.setPen(QPen(color, 1)) rect = self.contentsRect() center = rect.center() From 0a535f13bca4bde6f9ba7f7dd82ec3691882fa78 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 14 Oct 2021 16:05:37 +0200 Subject: [PATCH 09/20] owhierarchicalclustering: Use palette colors --- Orange/widgets/unsupervised/owhierarchicalclustering.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Orange/widgets/unsupervised/owhierarchicalclustering.py b/Orange/widgets/unsupervised/owhierarchicalclustering.py index 099dec5f2b9..3f533a45a85 100644 --- a/Orange/widgets/unsupervised/owhierarchicalclustering.py +++ b/Orange/widgets/unsupervised/owhierarchicalclustering.py @@ -11,11 +11,9 @@ QAction, QComboBox, QGraphicsGridLayout, QGraphicsSceneMouseEvent ) from AnyQt.QtGui import QColor, QPen, QFont, QKeySequence -from AnyQt.QtCore import Qt, QSize, QSizeF, QPointF, QRectF, QLineF, QEvent +from AnyQt.QtCore import Qt, QSizeF, QPointF, QRectF, QLineF, QEvent from AnyQt.QtCore import pyqtSignal as Signal, pyqtSlot as Slot -import pyqtgraph as pg - import Orange.data from Orange.data.domain import filter_visible from Orange.data import Domain @@ -30,6 +28,7 @@ from Orange.widgets.utils.annotated_data import (create_annotated_table, ANNOTATED_DATA_SIGNAL_NAME) from Orange.widgets.utils.widgetpreview import WidgetPreview +from Orange.widgets.visualize.utils.plotutils import AxisItem from Orange.widgets.widget import Input, Output, Msg from Orange.widgets.utils.stickygraphicsview import StickyGraphicsView @@ -867,7 +866,7 @@ def qfont_scaled(font, factor): return scaled -class AxisItem(pg.AxisItem): +class AxisItem(AxisItem): mousePressed = Signal(QPointF, Qt.MouseButton) mouseMoved = Signal(QPointF, Qt.MouseButtons) mouseReleased = Signal(QPointF, Qt.MouseButton) From f6fc6a4257d09b7222bd7a27b648503846f63590 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Mon, 18 Oct 2021 10:19:01 +0200 Subject: [PATCH 10/20] Use PlotWidget --- Orange/widgets/data/owpaintdata.py | 4 ++-- Orange/widgets/unsupervised/owcorrespondence.py | 10 +++++----- Orange/widgets/visualize/owbarplot.py | 6 +++--- Orange/widgets/visualize/owdistributions.py | 5 +++-- Orange/widgets/visualize/owlineplot.py | 6 +++--- Orange/widgets/visualize/owviolinplot.py | 6 +++--- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Orange/widgets/data/owpaintdata.py b/Orange/widgets/data/owpaintdata.py index bf254897d5a..372a039a8e9 100644 --- a/Orange/widgets/data/owpaintdata.py +++ b/Orange/widgets/data/owpaintdata.py @@ -30,6 +30,7 @@ from Orange.util import scale, namegen from Orange.widgets.utils.widgetpreview import WidgetPreview +from Orange.widgets.visualize.utils.plotutils import PlotWidget from Orange.widgets.widget import OWWidget, Msg, Input, Output @@ -939,8 +940,7 @@ def _init_ui(self): # main area GUI viewbox = PaintViewBox(enableMouse=False) - self.plotview = pg.PlotWidget(background=None, viewBox=viewbox) - self.plotview.setBackgroundRole(QPalette.Base) + self.plotview = PlotWidget(viewBox=viewbox) self.plot = self.plotview.getPlotItem() axis_color = self.palette().color(QPalette.Text) diff --git a/Orange/widgets/unsupervised/owcorrespondence.py b/Orange/widgets/unsupervised/owcorrespondence.py index feba1a20194..1b4b578ba67 100644 --- a/Orange/widgets/unsupervised/owcorrespondence.py +++ b/Orange/widgets/unsupervised/owcorrespondence.py @@ -6,18 +6,18 @@ from AnyQt.QtWidgets import QListView, QApplication, QSizePolicy from AnyQt.QtGui import QBrush, QColor, QPainter, QPalette from AnyQt.QtCore import QEvent, Qt -from orangewidget.utils.listview import ListViewSearch import pyqtgraph as pg + +from orangewidget.utils.listview import ListViewSearch + from Orange.data import Table, Domain, ContinuousVariable, StringVariable from Orange.statistics import contingency - from Orange.widgets import widget, gui, settings from Orange.widgets.utils import itemmodels, colorpalettes from Orange.widgets.utils.itemmodels import select_rows from Orange.widgets.utils.widgetpreview import WidgetPreview - -from Orange.widgets.visualize.owscatterplotgraph import ScatterPlotItem +from Orange.widgets.visualize.utils.plotutils import PlotWidget from Orange.widgets.widget import Input, Output from Orange.widgets.settings import Setting @@ -93,7 +93,7 @@ def __init__(self): gui.auto_send(self.buttonsArea, self, "auto_commit") - self.plot = pg.PlotWidget(background="w") + self.plot = PlotWidget() self.plot.setMenuEnabled(False) self.mainArea.layout().addWidget(self.plot) diff --git a/Orange/widgets/visualize/owbarplot.py b/Orange/widgets/visualize/owbarplot.py index df0e9dc2644..578ddb2af77 100644 --- a/Orange/widgets/visualize/owbarplot.py +++ b/Orange/widgets/visualize/owbarplot.py @@ -28,7 +28,7 @@ from Orange.widgets.visualize.utils.customizableplot import Updater, \ CommonParameterSetter from Orange.widgets.visualize.utils.plotutils import AxisItem, \ - HelpEventDelegate + HelpEventDelegate, PlotWidget from Orange.widgets.widget import OWWidget, Input, Output, Msg MAX_INSTANCES = 200 @@ -140,7 +140,7 @@ def legend_items(self): return self.master.legend.items -class BarPlotGraph(pg.PlotWidget): +class BarPlotGraph(PlotWidget): selection_changed = Signal(list) bar_width = 0.7 @@ -152,7 +152,7 @@ def __init__(self, master, parent=None): super().__init__( parent=parent, viewBox=BarPlotViewBox(self), - background="w", enableMenu=False, + enableMenu=False, axisItems={"bottom": AxisItem(orientation="bottom", rotate_ticks=True), "left": AxisItem(orientation="left")} diff --git a/Orange/widgets/visualize/owdistributions.py b/Orange/widgets/visualize/owdistributions.py index 5795036da51..492d11e153c 100644 --- a/Orange/widgets/visualize/owdistributions.py +++ b/Orange/widgets/visualize/owdistributions.py @@ -21,7 +21,8 @@ create_groups_table, create_annotated_table, ANNOTATED_DATA_SIGNAL_NAME from Orange.widgets.utils.itemmodels import DomainModel from Orange.widgets.utils.widgetpreview import WidgetPreview -from Orange.widgets.visualize.utils.plotutils import ElidedLabelsAxis +from Orange.widgets.visualize.utils.plotutils import ElidedLabelsAxis, \ + PlotWidget from Orange.widgets.widget import Input, Output, OWWidget, Msg from Orange.widgets.visualize.owscatterplotgraph import \ @@ -168,7 +169,7 @@ def boundingRect(self): return QRectF(self.x, 0, self.width, height) -class DistributionWidget(pg.PlotWidget): +class DistributionWidget(PlotWidget): item_clicked = Signal(DistributionBarItem, Qt.KeyboardModifiers, bool) blank_clicked = Signal() mouse_released = Signal() diff --git a/Orange/widgets/visualize/owlineplot.py b/Orange/widgets/visualize/owlineplot.py index dd9969c2608..b960ec8172e 100644 --- a/Orange/widgets/visualize/owlineplot.py +++ b/Orange/widgets/visualize/owlineplot.py @@ -32,7 +32,7 @@ from Orange.widgets.visualize.owdistributions import LegendItem from Orange.widgets.visualize.utils.customizableplot import Updater, \ CommonParameterSetter -from Orange.widgets.visualize.utils.plotutils import AxisItem +from Orange.widgets.visualize.utils.plotutils import AxisItem, PlotWidget from Orange.widgets.widget import OWWidget, Input, Output, Msg @@ -349,7 +349,7 @@ def getAxis(self): return self.master.getAxis # Customizable plot widget -class LinePlotGraph(pg.PlotWidget): +class LinePlotGraph(PlotWidget): def __init__(self, parent): self.groups: List[ProfileGroup] = [] self.bottom_axis = BottomAxisItem(orientation="bottom") @@ -357,7 +357,7 @@ def __init__(self, parent): left_axis = AxisItem(orientation="left") left_axis.setLabel("") super().__init__(parent, viewBox=LinePlotViewBox(), - background="w", enableMenu=False, + enableMenu=False, axisItems={"bottom": self.bottom_axis, "left": left_axis}) self.view_box = self.getViewBox() diff --git a/Orange/widgets/visualize/owviolinplot.py b/Orange/widgets/visualize/owviolinplot.py index 42270578e19..7de9d6e743c 100644 --- a/Orange/widgets/visualize/owviolinplot.py +++ b/Orange/widgets/visualize/owviolinplot.py @@ -29,7 +29,7 @@ from Orange.widgets.visualize.owboxplot import SortProxyModel from Orange.widgets.visualize.utils.customizableplot import \ CommonParameterSetter, Updater -from Orange.widgets.visualize.utils.plotutils import AxisItem +from Orange.widgets.visualize.utils.plotutils import AxisItem, PlotWidget from Orange.widgets.widget import OWWidget, Input, Output, Msg # scaling types @@ -369,7 +369,7 @@ def paint(self, painter: QPainter, *_): painter.restore() -class ViolinPlot(pg.PlotWidget): +class ViolinPlot(PlotWidget): VIOLIN_PADDING_FACTOR = 1.25 SELECTION_PADDING_FACTOR = 1.20 selection_changed = Signal(list, list) @@ -405,7 +405,7 @@ def __init__(self, parent: OWWidget, kernel: str, scale: int, view_box = ViolinPlotViewBox(self) super().__init__(parent, viewBox=view_box, - background="w", enableMenu=False, + enableMenu=False, axisItems={"bottom": AxisItem("bottom"), "left": AxisItem("left")}) self.setAntialiasing(True) From b1afbd73e5c25ef97fa246edde8b6e5ad0e39b4c Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Mon, 18 Oct 2021 10:20:24 +0200 Subject: [PATCH 11/20] slidergraph: Use palette --- Orange/widgets/utils/slidergraph.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Orange/widgets/utils/slidergraph.py b/Orange/widgets/utils/slidergraph.py index e18327381ef..ddb6df5375d 100644 --- a/Orange/widgets/utils/slidergraph.py +++ b/Orange/widgets/utils/slidergraph.py @@ -1,9 +1,11 @@ import numpy as np -from pyqtgraph import PlotWidget, mkPen, InfiniteLine, PlotCurveItem, \ +from pyqtgraph import mkPen, InfiniteLine, PlotCurveItem, \ TextItem, Point from AnyQt.QtGui import QColor from AnyQt.QtCore import Qt +from Orange.widgets.visualize.utils.plotutils import PlotWidget + class SliderGraph(PlotWidget): """ @@ -12,7 +14,7 @@ class SliderGraph(PlotWidget): the line is moved a callback function is called with selected value (on x axis). - Attributes + Parameters ---------- x_axis_label : str A text label for x axis @@ -20,12 +22,10 @@ class SliderGraph(PlotWidget): A text label for y axis callback : callable A function which is called when selection is changed. - background : str, optional (default: "w") - Plot background color """ - def __init__(self, x_axis_label, y_axis_label, callback): - super().__init__(background="w") + def __init__(self, x_axis_label, y_axis_label, callback, **kwargs): + super().__init__(**kwargs) axis = self.getAxis("bottom") axis.setLabel(x_axis_label) @@ -83,7 +83,8 @@ def update(self, x, y, colors, cutpoint_x=None, selection_limit=None, self.selection_limit = selection_limit self.data_increasing = [np.sum(d[1:] - d[:-1]) > 0 for d in y] - + foreground = self.palette().text().color() + foreground.setAlpha(128) # plot sequence for s, c, n, inc in zip(y, colors, names, self.data_increasing): c = QColor(c) @@ -91,7 +92,7 @@ def update(self, x, y, colors, cutpoint_x=None, selection_limit=None, if n is not None: label = TextItem( - text=n, anchor=(0, 1), color=QColor(0, 0, 0, 128)) + text=n, anchor=(0, 1), color=foreground) label.setPos(x[-1], s[-1]) self._set_anchor(label, len(x) - 1, inc) self.addItem(label) @@ -142,7 +143,7 @@ def _plot_cutpoint(self, x): else (self.x.min(), self.x.max()) ) self._line.setCursor(Qt.SizeHorCursor) - self._line.setPen(mkPen(QColor(Qt.black), width=2)) + self._line.setPen(mkPen(self.palette().text().color(), width=2)) self._line.sigPositionChanged.connect(self._on_cut_changed) self.addItem(self._line) else: @@ -155,11 +156,13 @@ def _plot_horizontal_lines(self): Function plots the vertical dashed lines that points to the selected sequence values at the y axis. """ + highlight = self.palette().highlight() + text = self.palette().text() for _ in range(len(self.sequences)): self.plot_horline.append(PlotCurveItem( - pen=mkPen(QColor(Qt.blue), style=Qt.DashLine))) + pen=mkPen(highlight.color(), style=Qt.DashLine))) self.plot_horlabel.append(TextItem( - color=QColor(Qt.black), anchor=(0, 1))) + color=text.color(), anchor=(0, 1))) for item in self.plot_horlabel + self.plot_horline: self.addItem(item) From 384f6f4a292068d97784d22b7c1d058d35082efd Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Mon, 18 Oct 2021 12:08:55 +0200 Subject: [PATCH 12/20] AnchorItem: Change parent class to GraphicsWidget --- Orange/widgets/visualize/utils/plotutils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Orange/widgets/visualize/utils/plotutils.py b/Orange/widgets/visualize/utils/plotutils.py index 8a0a2395b6d..e92e1fd5fc5 100644 --- a/Orange/widgets/visualize/utils/plotutils.py +++ b/Orange/widgets/visualize/utils/plotutils.py @@ -33,7 +33,7 @@ def get_xy(self): return point.x(), point.y() -class AnchorItem(pg.GraphicsObject): +class AnchorItem(pg.GraphicsWidget): def __init__(self, parent=None, line=QLineF(), text="", **kwargs): super().__init__(parent, **kwargs) self._text = text @@ -50,6 +50,7 @@ def __init__(self, parent=None, line=QLineF(), text="", **kwargs): self._label = TextItem(text=text, color=(10, 10, 10)) self._label.setParentItem(self) self._label.setPos(*self.get_xy()) + self._label.setColor(self.palette().color(QPalette.Text)) if parent is not None: self.setParentItem(parent) @@ -123,6 +124,11 @@ def __updateLayout(self): self._arrow.setPos(self._spine.line().p2()) self._arrow.setRotation(180 - angle) + def changeEvent(self, event): + if event.type() == QEvent.PaletteChange: + self._label.setColor(self.palette().color(QPalette.Text)) + super().changeEvent(event) + class HelpEventDelegate(QObject): def __init__(self, delegate, parent=None): From 673bc4cfc905d6f1745d4d96b14cad79ad839e43 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Tue, 19 Oct 2021 10:37:21 +0200 Subject: [PATCH 13/20] owscatterplotgraph: Use palette colors for selection tips --- Orange/widgets/visualize/owscatterplotgraph.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Orange/widgets/visualize/owscatterplotgraph.py b/Orange/widgets/visualize/owscatterplotgraph.py index 98b0b0f54b9..0970794af36 100644 --- a/Orange/widgets/visualize/owscatterplotgraph.py +++ b/Orange/widgets/visualize/owscatterplotgraph.py @@ -626,7 +626,9 @@ def _create_drag_tooltip(self): r = text.boundingRect() text.setTextWidth(r.width()) rect = QGraphicsRectItem(0, 0, r.width() + 8, r.height() + 4) - rect.setBrush(QColor(224, 224, 224, 212)) + color = self.plot_widget.palette().color(QPalette.Disabled, QPalette.Window) + color.setAlpha(212) + rect.setBrush(color) rect.setPen(QPen(Qt.NoPen)) self.update_tooltip() From 0a8f5282206863bdc6d3889bb3362f4b9e492850 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Thu, 21 Oct 2021 23:04:57 +0200 Subject: [PATCH 14/20] plotutils: Add palette aware GraphicsView, PlotItem --- Orange/widgets/visualize/utils/plotutils.py | 49 +++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/Orange/widgets/visualize/utils/plotutils.py b/Orange/widgets/visualize/utils/plotutils.py index e92e1fd5fc5..cf8dcf31462 100644 --- a/Orange/widgets/visualize/utils/plotutils.py +++ b/Orange/widgets/visualize/utils/plotutils.py @@ -625,13 +625,56 @@ def setScene(self, scene): self.__updateScenePalette() def changeEvent(self, event): - if event.type() == QEvent.PaletteChange and \ - self.scene() is not None and self.scene().parent() is self: + if event.type() == QEvent.PaletteChange: + self.__updateScenePalette() + self.resetCachedContent() + super().changeEvent(event) + + def __updateScenePalette(self): + scene = self.scene() + if scene is not None: + scene.setPalette(self.palette()) + + +class GraphicsView(pg.GraphicsView): + """ + A pyqtgraph.GraphicsView with better QPalette integration. + + A default constructed plot will respect and adapt to the current palette + """ + def __init__(self, *args, background=None, **kwargs): + super().__init__(*args, background=background, **kwargs) + if background is None: + # Revert the pg.PlotWidget's modifications, use default + # for QGraphicsView + self.setBackgroundRole(QPalette.Base) + self.__updateScenePalette() + + def setScene(self, scene): + super().setScene(scene) + self.__updateScenePalette() + + def changeEvent(self, event): + if event.type() == QEvent.PaletteChange: self.__updateScenePalette() self.resetCachedContent() + super().changeEvent(event) def __updateScenePalette(self): scene = self.scene() - if scene is not None and scene.palette() != self.palette(): + if scene is not None: scene.setPalette(self.palette()) + + +class PlotItem(pg.PlotItem): + """ + A pyqtgraph.PlotItem with better QPalette integration. + + A default constructed plot will respect and adapt to the current palette + """ + def __init__(self, *args, **kwargs): + axisItems = kwargs.pop("axisItems", None) + if axisItems is None: + axisItems = {"left": AxisItem("left"), "bottom": AxisItem("bottom")} + super().__init__(*args, axisItems=axisItems, **kwargs) From 2d3d9d6e92a12611faf200c48e106db8baaabee1 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Fri, 22 Oct 2021 14:17:12 +0200 Subject: [PATCH 15/20] Use plotutils.GraphicsView and PlotItem --- Orange/widgets/evaluate/owcalibrationplot.py | 12 +++--------- Orange/widgets/evaluate/owliftcurve.py | 15 ++++----------- Orange/widgets/evaluate/owrocanalysis.py | 11 +++-------- Orange/widgets/unsupervised/owdistancemap.py | 4 ++-- 4 files changed, 12 insertions(+), 30 deletions(-) diff --git a/Orange/widgets/evaluate/owcalibrationplot.py b/Orange/widgets/evaluate/owcalibrationplot.py index 885705b2cb9..72c2822d8c1 100644 --- a/Orange/widgets/evaluate/owcalibrationplot.py +++ b/Orange/widgets/evaluate/owcalibrationplot.py @@ -3,7 +3,6 @@ import numpy as np from AnyQt.QtCore import Qt, QSize -from AnyQt.QtGui import QPalette, QPen from AnyQt.QtWidgets import QListWidget, QSizePolicy import pyqtgraph as pg @@ -22,7 +21,7 @@ from Orange.widgets.utils.widgetpreview import WidgetPreview from Orange.widgets.visualize.utils.customizableplot import \ CommonParameterSetter -from Orange.widgets.visualize.utils.plotutils import AxisItem +from Orange.widgets.visualize.utils.plotutils import GraphicsView, PlotItem from Orange.widgets.widget import Input, Output, Msg from Orange.widgets import report @@ -192,18 +191,13 @@ def __init__(self): gui.auto_apply(self.buttonsArea, self, "auto_commit") - self.plotview = pg.GraphicsView(background=None) - self.plotview.setBackgroundRole(QPalette.Base) - axes = {"bottom": AxisItem(orientation="bottom"), - "left": AxisItem(orientation="left")} - self.plot = pg.PlotItem(enableMenu=False, axisItems=axes) + self.plotview = GraphicsView() + self.plot = PlotItem(enableMenu=False) self.plot.parameter_setter = ParameterSetter(self.plot) self.plot.setMouseEnabled(False, False) self.plot.hideButtons() - pen = QPen(self.palette().color(QPalette.Text)) for axis_name in ("bottom", "left"): axis = self.plot.getAxis(axis_name) - axis.setPen(pen) # Remove the condition (that is, allow setting this for bottom # axis) when pyqtgraph is fixed # Issue: https://github.com/pyqtgraph/pyqtgraph/issues/930 diff --git a/Orange/widgets/evaluate/owliftcurve.py b/Orange/widgets/evaluate/owliftcurve.py index bd70b9da618..1da801e9f87 100644 --- a/Orange/widgets/evaluate/owliftcurve.py +++ b/Orange/widgets/evaluate/owliftcurve.py @@ -4,7 +4,7 @@ import numpy as np from AnyQt.QtWidgets import QListView, QFrame -from AnyQt.QtGui import QColor, QPen, QPalette, QFont +from AnyQt.QtGui import QColor, QPen, QFont from AnyQt.QtCore import Qt import pyqtgraph as pg @@ -22,7 +22,7 @@ from Orange.widgets.utils.widgetpreview import WidgetPreview from Orange.widgets.visualize.utils.customizableplot import Updater, \ CommonParameterSetter -from Orange.widgets.visualize.utils.plotutils import AxisItem +from Orange.widgets.visualize.utils.plotutils import GraphicsView, PlotItem from Orange.widgets.widget import Input from Orange.widgets import report @@ -214,13 +214,9 @@ def __init__(self): gui.rubber(self.controlArea) - self.plotview = pg.GraphicsView(background=None) - self.plotview.setBackgroundRole(QPalette.Base) + self.plotview = GraphicsView() self.plotview.setFrameStyle(QFrame.StyledPanel) - - axes = {"bottom": AxisItem(orientation="bottom"), - "left": AxisItem(orientation="left")} - self.plot = pg.PlotItem(enableMenu=False, axisItems=axes) + self.plot = PlotItem(enableMenu=False) self.plot.parameter_setter = ParameterSetter(self.plot) self.plot.curve_items = [] self.plot.hull_items = [] @@ -229,15 +225,12 @@ def __init__(self): self.plot.setMouseEnabled(False, False) self.plot.hideButtons() - pen = QPen(self.palette().color(QPalette.Text)) - tickfont = QFont(self.font()) tickfont.setPixelSize(max(int(tickfont.pixelSize() * 2 // 3), 11)) for pos, label in (("bottom", "P Rate"), ("left", "")): axis = self.plot.getAxis(pos) axis.setTickFont(tickfont) - axis.setPen(pen) axis.setLabel(label) self._set_left_label() diff --git a/Orange/widgets/evaluate/owrocanalysis.py b/Orange/widgets/evaluate/owrocanalysis.py index 5daba08cc1f..8c4a6725136 100644 --- a/Orange/widgets/evaluate/owrocanalysis.py +++ b/Orange/widgets/evaluate/owrocanalysis.py @@ -19,6 +19,7 @@ from Orange.widgets.evaluate.utils import check_results_adequacy from Orange.widgets.utils import colorpalettes from Orange.widgets.utils.widgetpreview import WidgetPreview +from Orange.widgets.visualize.utils.plotutils import GraphicsView, PlotItem from Orange.widgets.widget import Input from Orange.widgets import report @@ -395,30 +396,24 @@ def __init__(self): grid.addWidget(QLabel("Prior probability:")) grid.addWidget(self.target_prior_sp, 2, 1) - self.plotview = pg.GraphicsView(background=None) - self.plotview.setBackgroundRole(QPalette.Base) + self.plotview = GraphicsView(background=None) self.plotview.setFrameStyle(QFrame.StyledPanel) self.plotview.scene().sigMouseMoved.connect(self._on_mouse_moved) - self.plotview.scene().setPalette(self.palette()) - self.plot = pg.PlotItem(enableMenu=False) + self.plot = PlotItem(enableMenu=False) self.plot.setMouseEnabled(False, False) self.plot.hideButtons() - pen = QPen(self.palette().color(QPalette.Text)) - tickfont = QFont(self.font()) tickfont.setPixelSize(max(int(tickfont.pixelSize() * 2 // 3), 11)) axis = self.plot.getAxis("bottom") axis.setTickFont(tickfont) - axis.setPen(pen) axis.setLabel("FP Rate (1-Specificity)") axis.setGrid(16) axis = self.plot.getAxis("left") axis.setTickFont(tickfont) - axis.setPen(pen) axis.setLabel("TP Rate (Sensitivity)") axis.setGrid(16) diff --git a/Orange/widgets/unsupervised/owdistancemap.py b/Orange/widgets/unsupervised/owdistancemap.py index d7d86d08541..de4047e48e8 100644 --- a/Orange/widgets/unsupervised/owdistancemap.py +++ b/Orange/widgets/unsupervised/owdistancemap.py @@ -7,7 +7,7 @@ from AnyQt.QtWidgets import ( QGraphicsRectItem, QGraphicsGridLayout, QApplication, QSizePolicy ) -from AnyQt.QtGui import QFontMetrics, QPen, QTransform, QFont, QPalette +from AnyQt.QtGui import QFontMetrics, QPen, QTransform, QFont from AnyQt.QtCore import Qt, QRect, QRectF, QPointF from AnyQt.QtCore import pyqtSignal as Signal @@ -28,6 +28,7 @@ from Orange.widgets.visualize.utils.plotutils import HelpEventDelegate from Orange.widgets.widget import Input, Output from Orange.widgets.utils.dendrogram import DendrogramWidget +from Orange.widgets.visualize.utils.plotutils import GraphicsView from Orange.widgets.visualize.utils.heatmap import ( GradientColorMap, GradientLegendWidget, ) @@ -345,7 +346,6 @@ def _set_thresholds(low, high): gui.auto_send(self.buttonsArea, self, "autocommit") self.view = GraphicsView(background=None) - self.view.setBackgroundRole(QPalette.Base) self.mainArea.layout().addWidget(self.view) self.grid_widget = pg.GraphicsWidget() From d1abedb906be3fb2128f7b178aa939398eaf49d1 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Fri, 22 Oct 2021 14:10:06 +0200 Subject: [PATCH 16/20] owscatterplotgraph: Use palette colors for legend item --- .../widgets/visualize/owscatterplotgraph.py | 51 ++++++++++++++----- Orange/widgets/visualize/utils/plotutils.py | 3 +- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Orange/widgets/visualize/owscatterplotgraph.py b/Orange/widgets/visualize/owscatterplotgraph.py index 0970794af36..e66e4fc6000 100644 --- a/Orange/widgets/visualize/owscatterplotgraph.py +++ b/Orange/widgets/visualize/owscatterplotgraph.py @@ -7,7 +7,7 @@ import numpy as np from AnyQt.QtCore import Qt, QRectF, QSize, QTimer, pyqtSignal as Signal, \ - QObject + QObject, QEvent from AnyQt.QtGui import QColor, QPen, QBrush, QPainterPath, QTransform, \ QPainter, QPalette from AnyQt.QtWidgets import QApplication, QToolTip, QGraphicsTextItem, \ @@ -38,20 +38,19 @@ class LegendItem(PgLegendItem): - def __init__(self, size=None, offset=None, pen=None, brush=None): + def __init__( + self, size=None, offset=None, pen=None, brush=None, + ): super().__init__(size, offset) self.layout.setContentsMargins(5, 5, 5, 5) self.layout.setHorizontalSpacing(15) self.layout.setColumnAlignment(1, Qt.AlignLeft | Qt.AlignVCenter) - - if pen is None: - pen = QPen(QColor(196, 197, 193, 200), 1) - pen.setCosmetic(True) + if pen is not None: + pen = QPen(pen) + if brush is not None: + brush = QBrush(brush) self.__pen = pen - - if brush is None: - brush = QBrush(QColor(232, 232, 232, 100)) self.__brush = brush def restoreAnchor(self, anchors): @@ -65,16 +64,17 @@ def restoreAnchor(self, anchors): # pylint: disable=arguments-differ def paint(self, painter, _option, _widget=None): - painter.setPen(self.__pen) - painter.setBrush(self.__brush) + painter.setPen(self.pen()) + painter.setBrush(self.brush()) rect = self.contentsRect() painter.drawRoundedRect(rect, 2, 2) def addItem(self, item, name): super().addItem(item, name) - # Fix-up the label alignment + # Fix-up the label alignment, and color + color = self.palette().color(QPalette.Text) _, label = self.items[-1] - label.setText(name, justify="left") + label.setText(name, justify="left", color=color) def clear(self): """ @@ -90,6 +90,31 @@ def clear(self): self.updateSize() + def pen(self): + if self.__pen is not None: + return QPen(self.__pen) + else: + color = self.palette().color(QPalette.Disabled, QPalette.Text) + color.setAlpha(100) + pen = QPen(color, 1) + pen.setCosmetic(True) + return pen + + def brush(self): + if self.__brush is not None: + return QBrush(self.__brush) + else: + color = self.palette().color(QPalette.Window) + color.setAlpha(150) + return QBrush(color) + + def changeEvent(self, event: QEvent): + if event.type() == QEvent.PaletteChange: + color = self.palette().color(QPalette.Text) + for _, label in self.items: + label.setText(label.text, color=color) + super().changeEvent(event) + def bound_anchor_pos(corner, parentpos): corner = np.clip(corner, 0, 1) diff --git a/Orange/widgets/visualize/utils/plotutils.py b/Orange/widgets/visualize/utils/plotutils.py index cf8dcf31462..c7ac22a9fe3 100644 --- a/Orange/widgets/visualize/utils/plotutils.py +++ b/Orange/widgets/visualize/utils/plotutils.py @@ -481,12 +481,13 @@ def paint(self, p, *args): p.translate(5, 5) p.setFont(self.font) colors = self.palette.qcolors + foreground = super().palette().color(QPalette.Text) h = self.bin_height for i, color, label in zip(itertools.count(), colors, self.labels): p.setPen(Qt.NoPen) p.setBrush(QBrush(color)) p.drawRect(0, i * h, h, h) - p.setPen(QPen(Qt.black)) + p.setPen(QPen(foreground)) p.drawStaticText(h + 5, i * h + 1, label) From cf3554818f6f06ca222ee1beafeb706fce18fbaf Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Mon, 25 Oct 2021 12:18:09 +0200 Subject: [PATCH 17/20] heatmap: Adapt to palette change --- Orange/widgets/visualize/utils/heatmap.py | 41 +++++++++++++------ .../visualize/utils/tests/test_heatmap.py | 10 ++++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/Orange/widgets/visualize/utils/heatmap.py b/Orange/widgets/visualize/utils/heatmap.py index 29c9e0069ca..29782bb946e 100644 --- a/Orange/widgets/visualize/utils/heatmap.py +++ b/Orange/widgets/visualize/utils/heatmap.py @@ -19,8 +19,6 @@ QGraphicsLayoutItem ) -import pyqtgraph as pg - from Orange.clustering import hierarchical from Orange.clustering.hierarchical import Tree from Orange.widgets.utils import apply_all @@ -32,6 +30,7 @@ from Orange.widgets.utils.graphicstextlist import TextListWidget from Orange.widgets.utils.dendrogram import DendrogramWidget +from Orange.widgets.visualize.utils.plotutils import AxisItem def leaf_indices(tree: Tree) -> Sequence[int]: @@ -936,6 +935,11 @@ def setShowAverages(self, visible): item.setVisible(visible) item.setPreferredWidth(0 if not visible else 10) + def changeEvent(self, event): + if event.type() == QEvent.PaletteChange: + self.__update_palette() + super().changeEvent(event) + def event(self, event): # type: (QEvent) -> bool rval = super().event(event) @@ -992,6 +996,12 @@ def __select_by_cluster(self, item, dendrogramindex): node.value.first, node.value.last - 1, hm, clear=clear, remove=remove, append=append) + def __update_palette(self): + for item in layout_items_recursive(self.layout()): + if isinstance(item, SimpleLayoutItem) \ + and isinstance(item.item, QGraphicsSimpleTextItem): + item.item.setBrush(self.palette().text()) + def heatmapAtPos(self, pos: QPointF) -> Optional['GraphicsHeatmapWidget']: for hw in chain.from_iterable(self.heatmap_widget_grid): if hw.contains(hw.mapFromItem(self, pos)): @@ -1234,7 +1244,7 @@ def remove_item(item: QGraphicsItem) -> None: item.setParentItem(None) -class _GradientLegendAxisItem(pg.AxisItem): +class _GradientLegendAxisItem(AxisItem): def boundingRect(self): br = super().boundingRect() if self.orientation in ["top", "bottom"]: @@ -1335,12 +1345,6 @@ def __update(self): self.updateGeometry() - def changeEvent(self, event: QEvent) -> None: - if event.type() == QEvent.PaletteChange: - pen = QPen(self.palette().color(QPalette.Text)) - self.__axis.setPen(pen) - super().changeEvent(event) - class CategoricalColorLegend(QGraphicsWidget): def __init__( @@ -1431,20 +1435,31 @@ def legend_item_pair(color: QColor, size: float, text: str): def changeEvent(self, event: QEvent) -> None: if event.type() == QEvent.FontChange: self._updateFont(self.font()) + elif event.type() == QEvent.PaletteChange: + self._updatePalette() super().changeEvent(event) def _updateFont(self, font): w = QFontMetrics(font).horizontalAdvance("X") - for item in filter( - lambda item: isinstance(item, SimpleLayoutItem), - layout_items_recursive(self.__layout) - ): + for item in self.__layoutItems(): if isinstance(item.item, QGraphicsSimpleTextItem): item.item.setFont(font) elif isinstance(item.item, QGraphicsRectItem): item.item.setRect(QRectF(0, 0, w, w)) item.updateGeometry() + def _updatePalette(self): + palette = self.palette() + for item in self.__layoutItems(): + if isinstance(item.item, QGraphicsSimpleTextItem): + item.item.setBrush(palette.brush(QPalette.Text)) + + def __layoutItems(self): + return filter( + lambda item: isinstance(item, SimpleLayoutItem), + layout_items_recursive(self.__layout) + ) + def layout_items(layout: QGraphicsLayout) -> Iterable[QGraphicsLayoutItem]: for item in map(layout.itemAt, range(layout.count())): diff --git a/Orange/widgets/visualize/utils/tests/test_heatmap.py b/Orange/widgets/visualize/utils/tests/test_heatmap.py index 1aa48c6af4d..6d8e55d2233 100644 --- a/Orange/widgets/visualize/utils/tests/test_heatmap.py +++ b/Orange/widgets/visualize/utils/tests/test_heatmap.py @@ -1,7 +1,7 @@ import numpy as np from AnyQt.QtCore import Qt, QPoint -from AnyQt.QtGui import QFont +from AnyQt.QtGui import QFont, QPalette from AnyQt.QtTest import QTest, QSignalSpy from AnyQt.QtWidgets import QGraphicsScene, QGraphicsView @@ -93,6 +93,14 @@ def test_widget(self): w.headerGeometry() w.footerGeometry() + # Trigger the change events. + p = QPalette() + p.setColor(QPalette.All, QPalette.Text, Qt.red) + w.setPalette(p) + f = QFont() + f.setPointSizeF(19.5) + w.setFont(f) + def test_widget_annotations(self): w = HeatmapGridWidget() self.scene.addItem(w) From d19cb066dbe483b90a5de9d2cd8248c39e49230b Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Tue, 26 Oct 2021 12:33:37 +0200 Subject: [PATCH 18/20] owhierarchicalclustering: Inherit SliderLine from QGraphicsWidget Use palette color as the default pen color. --- .../unsupervised/owhierarchicalclustering.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Orange/widgets/unsupervised/owhierarchicalclustering.py b/Orange/widgets/unsupervised/owhierarchicalclustering.py index 3f533a45a85..ccda65b4092 100644 --- a/Orange/widgets/unsupervised/owhierarchicalclustering.py +++ b/Orange/widgets/unsupervised/owhierarchicalclustering.py @@ -7,10 +7,10 @@ import numpy as np from AnyQt.QtWidgets import ( - QGraphicsWidget, QGraphicsObject, QGraphicsScene, QGridLayout, QSizePolicy, + QGraphicsWidget, QGraphicsScene, QGridLayout, QSizePolicy, QAction, QComboBox, QGraphicsGridLayout, QGraphicsSceneMouseEvent ) -from AnyQt.QtGui import QColor, QPen, QFont, QKeySequence +from AnyQt.QtGui import QPen, QFont, QKeySequence, QPainterPath from AnyQt.QtCore import Qt, QSizeF, QPointF, QRectF, QLineF, QEvent from AnyQt.QtCore import pyqtSignal as Signal, pyqtSlot as Slot @@ -891,7 +891,7 @@ def mouseReleaseEvent(self, event): event.accept() -class SliderLine(QGraphicsObject): +class SliderLine(QGraphicsWidget): """A movable slider line.""" valueChanged = Signal(float) @@ -907,14 +907,10 @@ def __init__(self, parent=None, orientation=Qt.Vertical, value=0.0, self._length = length self._min = 0.0 self._max = 1.0 - self._line = QLineF() # type: Optional[QLineF] - self._pen = QPen() + self._line: Optional[QLineF] = QLineF() + self._pen: Optional[QPen] = None super().__init__(parent, **kwargs) - self.setAcceptedMouseButtons(Qt.LeftButton) - self.setPen(make_pen(brush=QColor(50, 50, 50), width=1, cosmetic=False, - style=Qt.DashLine)) - if self._orientation == Qt.Vertical: self.setCursor(Qt.SizeVerCursor) else: @@ -929,7 +925,10 @@ def setPen(self, pen: Union[QPen, Qt.GlobalColor, Qt.PenStyle]) -> None: self.update() def pen(self) -> QPen: - return QPen(self._pen) + if self._pen is None: + return QPen(self.palette().text(), 1.0, Qt.DashLine) + else: + return QPen(self._pen) def setValue(self, value: float): value = min(max(value, self._min), self._max) @@ -991,6 +990,11 @@ def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: self.lineReleased.emit() event.accept() + def shape(self) -> QPainterPath: + path = QPainterPath() + path.addRect(self.boundingRect()) + return path + def boundingRect(self) -> QRectF: if self._line is None: if self._orientation == Qt.Vertical: From 55ff04504d6b8fc9c9e9c23dcda0992ffa65cb0b Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Wed, 27 Oct 2021 10:56:19 +0200 Subject: [PATCH 19/20] owdistancematrix: Specify foreground when overriding background --- Orange/widgets/unsupervised/owdistancematrix.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Orange/widgets/unsupervised/owdistancematrix.py b/Orange/widgets/unsupervised/owdistancematrix.py index e75057f7c56..c043b0d52e1 100644 --- a/Orange/widgets/unsupervised/owdistancematrix.py +++ b/Orange/widgets/unsupervised/owdistancematrix.py @@ -69,7 +69,7 @@ def dimension(self, parent=None): def color_for_label(self, ind, light=100): if self.label_colors is None: - return Qt.lightGray + return None return QBrush(self.label_colors[ind].lighter(light)) def color_for_cell(self, row, col): @@ -80,7 +80,7 @@ def data(self, index, role=Qt.DisplayRole): return Qt.AlignRight | Qt.AlignVCenter row, col = index.row(), index.column() if self.distances is None: - return + return None if role == TableBorderItem.BorderColorRole: return self.color_for_label(col), self.color_for_label(row) if role == FixedFormatNumericColumnDelegate.ColumnDataSpanRole: @@ -93,15 +93,21 @@ def data(self, index, role=Qt.DisplayRole): return float(self.distances[row, col]) if role == Qt.BackgroundColorRole: return self.color_for_cell(row, col) + if role == Qt.ForegroundRole: + return QColor(Qt.black) # the background is light-ish + return None def headerData(self, ind, orientation, role): if not self.labels: - return + return None if role == Qt.DisplayRole and ind < len(self.labels): return self.labels[ind] # On some systems, Qt doesn't respect the following role in the header if role == Qt.BackgroundRole: return self.color_for_label(ind, 150) + if role == Qt.ForegroundRole and self.label_colors is not None: + return QColor(Qt.black) + return None class TableBorderItem(FixedFormatNumericColumnDelegate): @@ -194,7 +200,9 @@ def __init__(self): view.setWordWrap(False) view.setTextElideMode(Qt.ElideNone) view.setEditTriggers(QTableView.NoEditTriggers) - view.setItemDelegate(TableBorderItem(roles=(Qt.DisplayRole, Qt.BackgroundRole))) + view.setItemDelegate( + TableBorderItem( + roles=(Qt.DisplayRole, Qt.BackgroundRole, Qt.ForegroundRole))) view.setModel(self.tablemodel) view.setShowGrid(False) for header in (view.horizontalHeader(), view.verticalHeader()): From ca9b9731f81db3f7f90ed7c3ce3d740e13421481 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Fri, 4 Feb 2022 12:04:36 +0100 Subject: [PATCH 20/20] plotutils: Reset the palette in GraphicsView/PlotWidget constructors --- Orange/widgets/visualize/utils/plotutils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Orange/widgets/visualize/utils/plotutils.py b/Orange/widgets/visualize/utils/plotutils.py index c7ac22a9fe3..981cd42921d 100644 --- a/Orange/widgets/visualize/utils/plotutils.py +++ b/Orange/widgets/visualize/utils/plotutils.py @@ -619,6 +619,8 @@ def __init__(self, *args, background=None, **kwargs): # Revert the pg.PlotWidget's modifications, use default # for QGraphicsView background role self.setBackgroundRole(QPalette.Base) + # Reset changes to the palette (undo changes from pg.GraphicsView) + self.setPalette(QPalette()) self.__updateScenePalette() def setScene(self, scene): @@ -649,6 +651,8 @@ def __init__(self, *args, background=None, **kwargs): # Revert the pg.PlotWidget's modifications, use default # for QGraphicsView self.setBackgroundRole(QPalette.Base) + # Reset changes to the palette (undo changes from pg.GraphicsView) + self.setPalette(QPalette()) self.__updateScenePalette() def setScene(self, scene):