def test_step_space_continuous(self): """ Test step """ w = self.widget event = QKeyEvent(QEvent.KeyPress, Qt.Key_Space, Qt.KeyboardModifiers(0)) # with linear regression self.send_signal(w.Inputs.data, Table('housing')[::100]) # test theta set after step if not set yet w.keyPressEvent(event) self.assertIsNotNone(w.learner.theta) # check theta is changing when step old_theta = np.copy(w.learner.theta) w.keyPressEvent(event) self.assertNotEqual(sum(old_theta - w.learner.theta), 0) old_theta = np.copy(w.learner.theta) # to cover else example and check not crashes event = QKeyEvent(QEvent.KeyPress, Qt.Key_Q, Qt.KeyboardModifiers(0)) w.keyPressEvent(event) # check nothing changes assert_array_equal(old_theta, w.learner.theta)
def keySequenceClicks(widget_, keySequence, extraModifiers=Qt.NoModifier): """Use QTest.keyClick to send a QKeySequence to a widget.""" # pylint: disable=line-too-long # This is based on a simplified version of http://stackoverflow.com/questions/14034209/convert-string-representation-of-keycode-to-qtkey-or-any-int-and-back. I added code to handle the case in which the resulting key contains a modifier (for example, Shift+Home). When I execute QTest.keyClick(widget, keyWithModifier), I get the error "ASSERT: "false" in file .\qasciikey.cpp, line 495". To fix this, the following code splits the key into a key and its modifier. # Bitmask for all modifier keys. modifierMask = enum_as_int(Qt.KeyboardModifierMask) ks = QKeySequence(keySequence) # For now, we don't handle a QKeySequence("Ctrl") or any other modified by itself. assert ks.count() > 0 for _, key in enumerate(ks): key = key_code(key) modifiers = Qt.KeyboardModifiers((key & modifierMask) | enum_as_int(extraModifiers)) key = key & ~modifierMask QTest.keyClick(widget_, Qt.Key(key), modifiers, 10)
def __init__( self, parent: Optional[QGraphicsItem] = None, items: Iterable[str] = (), alignment: Union[Qt.AlignmentFlag, Qt.Alignment] = Qt.AlignLeading, orientation: Qt.Orientation = Qt.Vertical, **kwargs: Any ) -> None: sizePolicy = kwargs.pop( "sizePolicy", None) # type: Optional[QSizePolicy] super().__init__(None, **kwargs) self.setFlag(QGraphicsWidget.ItemClipsChildrenToShape, True) self.__items: List[str] = [] self.__textitems: List[QGraphicsSimpleTextItem] = [] self.__group: Optional[QGraphicsItemGroup] = None self.__spacing = 0 self.__alignment = Qt.AlignmentFlag(alignment) self.__orientation = orientation sp = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sp.setWidthForHeight(True) self.setSizePolicy(sp) if items is not None: self.setItems(items) if sizePolicy is not None: self.setSizePolicy(sizePolicy) if parent is not None: self.setParentItem(parent)
def _select_data(self): self.widget.attr_x, self.widget.attr_y = self.data.domain[:2] area = self.widget.areas[0] self.widget.select_area(area, QMouseEvent( QEvent.MouseButtonPress, QPoint(), Qt.LeftButton, Qt.LeftButton, Qt.KeyboardModifiers())) return [0, 4, 6, 7, 11, 17, 19, 21, 22, 24, 26, 39, 40, 43, 44, 46]
def __init__(self, parent=None, flags=Qt.WindowFlags(0), model=None, **kwargs): QDialog.__init__(self, parent, flags, **kwargs) self.__setupUi() if model is not None: self.setModel(model)
def methodSelectionChanged(self, state: int, method_name): state = Qt.CheckState(state) if state == Qt.Checked: self.selected_methods.add(method_name) elif method_name in self.selected_methods: self.selected_methods.remove(method_name) self.update_scores()
def mouseMove(widget, pos=QPoint(), delay=-1): # pragma: no-cover # Like QTest.mouseMove, but functional without QCursor.setPos if pos.isNull(): pos = widget.rect().center() me = QMouseEvent(QMouseEvent.MouseMove, pos, widget.mapToGlobal(pos), Qt.NoButton, Qt.MouseButtons(0), Qt.NoModifier) if delay > 0: QTest.qWait(delay) QApplication.sendEvent(widget, me)
def __init__(self, parent=None, flags=Qt.WindowFlags(0), model=None, **kwargs): # type: (Optional[QWidget], int, Optional[QAbstractItemModel], Any) -> None super().__init__(parent, flags, **kwargs) self.__setupUi() if model is not None: self.setModel(model)
def _select_data(self): self.widget.select_area( 1, QMouseEvent( QEvent.MouseButtonPress, QPoint(), Qt.LeftButton, Qt.LeftButton, Qt.KeyboardModifiers(), ), ) return [2, 3, 9, 23, 29, 30, 34, 35, 37, 42, 47, 49]
def test_selection_setting(self): widget = self.widget data = Table("iris.tab") self.send_signal(widget.Inputs.data, data) widget.select_area( 1, QMouseEvent(QEvent.MouseButtonPress, QPoint(), Qt.LeftButton, Qt.LeftButton, Qt.KeyboardModifiers())) # Changing the data must reset the selection self.send_signal(widget.Inputs.data, Table("titanic")) self.assertFalse(bool(widget.selection)) self.assertIsNone(self.get_output(widget.Outputs.selected_data)) self.send_signal(widget.Inputs.data, data) self.assertFalse(bool(widget.selection)) self.assertIsNone(self.get_output(widget.Outputs.selected_data)) widget.select_area( 1, QMouseEvent(QEvent.MouseButtonPress, QPoint(), Qt.LeftButton, Qt.LeftButton, Qt.KeyboardModifiers())) settings = self.widget.settingsHandler.pack_data(self.widget) # Setting data to None must reset the selection self.send_signal(widget.Inputs.data, None) self.assertFalse(bool(widget.selection)) self.assertIsNone(self.get_output(widget.Outputs.selected_data)) self.send_signal(widget.Inputs.data, data) self.assertFalse(bool(widget.selection)) self.assertIsNone(self.get_output(widget.Outputs.selected_data)) w = self.create_widget(OWMosaicDisplay, stored_settings=settings) self.assertFalse(bool(widget.selection)) self.send_signal(w.Inputs.data, data, widget=w) self.assertEqual(w.selection, {1}) self.assertIsNotNone(self.get_output(w.Outputs.selected_data, widget=w))
def nextCheckState(self, state, index): # type: (Qt.CheckState, QModelIndex) -> Qt.CheckState """ Return the next check state for index. """ constraint = index.data(HasConstraintRole) flags = index.flags() if flags & Qt.ItemIsUserTristate and constraint: return Qt.PartiallyChecked if state == Qt.Checked else Qt.Checked elif flags & Qt.ItemIsUserTristate: return Qt.CheckState((state + 1) % 3) else: return Qt.Unchecked if state == Qt.Checked else Qt.Checked
def flags(self, index): # type: (QModelIndex) -> Qt.ItemFlags """ Reimplemented from :class:`QSortFilterProxyModel.flags` """ source = self.mapToSource(index) flags = source.flags() if self.__filterFunc is not None: enabled = flags & Qt.ItemIsEnabled if enabled and not self.__filterFunc(source): flags = Qt.ItemFlags(flags ^ Qt.ItemIsEnabled) return flags
def __init__(self, parent=None, **kwargs): # type: (Optional[QGraphicsItem], Any) -> None super().__init__(None, **kwargs) self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemIsSelectable) self.setFocusPolicy(Qt.ClickFocus) self.__contentType = "text/plain" self.__content = "" self.__textMargins = (2, 2, 2, 2) self.__textInteractionFlags = Qt.NoTextInteraction self.__defaultInteractionFlags = Qt.TextInteractionFlags( Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard ) rect = self.geometry().translated(-self.pos()) self.__framePen = QPen(Qt.NoPen) self.__framePathItem = QGraphicsPathItem(self) self.__framePathItem.setPen(self.__framePen) self.__textItem = GraphicsTextEdit(self) self.__textItem.setOpenExternalLinks(True) self.__textItem.setPlaceholderText(self.tr("Enter text here")) self.__textItem.setPos(2, 2) self.__textItem.setTextWidth(rect.width() - 4) self.__textItem.setTabChangesFocus(True) self.__textItem.setTextInteractionFlags(self.__defaultInteractionFlags) self.__textItem.setFont(self.font()) self.__textItem.editingFinished.connect(self.__textEditingFinished) self.__textItem.setDefaultTextColor( self.palette().color(QPalette.Text) ) if self.__textItem.scene() is not None: self.__textItem.installSceneEventFilter(self) layout = self.__textItem.document().documentLayout() layout.documentSizeChanged.connect(self.__onDocumentSizeChanged) self.__updateFrame() # set parent item at the end in order to ensure # QGraphicsItem.ItemSceneHasChanged is delivered after initialization if parent is not None: self.setParentItem(parent)
def test_step_space_discrete(self): """ Test step """ w = self.widget event = QKeyEvent(QEvent.KeyPress, Qt.Key_Space, Qt.KeyboardModifiers(0)) # test function not crashes when no data and learner w.keyPressEvent(event) self.send_signal(w.Inputs.data, Table('iris')[::15]) # test theta set after step if not set yet w.keyPressEvent(event) self.assertIsNotNone(w.learner.theta) # check theta is changing when step old_theta = np.copy(w.learner.theta) w.keyPressEvent(event) self.assertNotEqual(sum(old_theta - w.learner.theta), 0)
def __init__(self, parent=None, anchor=Free, constraint=Qt.Orientation(0), **kwargs): # type: (Optional[QGraphicsItem], Anchor, Qt.Orientation, Any) -> None super().__init__(parent, **kwargs) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, False) self.setAcceptedMouseButtons(Qt.LeftButton) self.__constraint = constraint # type: Qt.Orientation self.__constraintFunc = None # type: Optional[ConstraintFunc] self.__anchor = ControlPoint.Free self.__initialPosition = None # type: Optional[QPointF] self.setAnchor(anchor) path = QPainterPath() path.addEllipse(QRectF(-4, -4, 8, 8)) self.setPath(path) self.setBrush(QBrush(Qt.lightGray, Qt.SolidPattern))
def __init__( self, parent: Optional[QGraphicsItem] = None, items: Iterable[str] = (), alignment: Union[Qt.AlignmentFlag, Qt.Alignment] = Qt.AlignLeading, orientation: Qt.Orientation = Qt.Vertical, autoScale=False, elideMode=Qt.ElideNone, **kwargs: Any ) -> None: self.__items: List[str] = [] self.__textitems: List[QGraphicsSimpleTextItem] = [] self.__group: Optional[QGraphicsItemGroup] = None self.__spacing = 0 self.__alignment = Qt.AlignmentFlag(alignment) self.__orientation = orientation self.__autoScale = autoScale # The effective font when autoScale is in effect self.__effectiveFont = QFont() self.__widthCache = {} self.__elideMode = elideMode sizePolicy = kwargs.pop( "sizePolicy", None) # type: Optional[QSizePolicy] super().__init__(None, **kwargs) self.setFlag(QGraphicsWidget.ItemClipsChildrenToShape, True) sp = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sp.setWidthForHeight(True) self.setSizePolicy(sp) if sizePolicy is not None: self.setSizePolicy(sizePolicy) if parent is not None: self.setParentItem(parent) if items is not None: self.setItems(items)
class QtWidgetRegistry(QObject, WidgetRegistry): """ A QObject wrapper for `WidgetRegistry` A QStandardItemModel instance containing the widgets in a tree (of depth 2). The items in a model can be quaries using standard roles (DisplayRole, BackgroundRole, DecorationRole ToolTipRole). They also have QtWidgetRegistry.CATEGORY_DESC_ROLE, QtWidgetRegistry.WIDGET_DESC_ROLE, which store Category/WidgetDescription respectfully. Furthermore QtWidgetRegistry.WIDGET_ACTION_ROLE stores an default QAction which can be used for widget creation action. """ CATEGORY_DESC_ROLE = Qt.ItemDataRole(Qt.UserRole + 1) """Category Description Role""" WIDGET_DESC_ROLE = Qt.ItemDataRole(Qt.UserRole + 2) """Widget Description Role""" WIDGET_ACTION_ROLE = Qt.ItemDataRole(Qt.UserRole + 3) """Widget Action Role""" BACKGROUND_ROLE = Qt.ItemDataRole(Qt.UserRole + 4) """Background color for widget/category in the canvas (different from Qt.BackgroundRole) """ category_added = Signal(str, CategoryDescription) """signal: category_added(name: str, desc: CategoryDescription) """ widget_added = Signal(str, str, WidgetDescription) """signal widget_added(category_name: str, widget_name: str, desc: WidgetDescription) """ reset = Signal() """signal: reset() """ def __init__(self, other_or_parent=None, parent=None): if isinstance(other_or_parent, QObject) and parent is None: parent, other_or_parent = other_or_parent, None QObject.__init__(self, parent) WidgetRegistry.__init__(self, other_or_parent) # Should the QStandardItemModel be subclassed? self.__item_model = QStandardItemModel(self) for i, desc in enumerate(self.categories()): cat_item = self._cat_desc_to_std_item(desc) self.__item_model.insertRow(i, cat_item) for j, wdesc in enumerate(self.widgets(desc.name)): widget_item = self._widget_desc_to_std_item(wdesc, desc) cat_item.insertRow(j, widget_item) def model(self): # type: () -> QStandardItemModel """ Return the widget descriptions in a Qt Item Model instance (QStandardItemModel). .. note:: The model should not be modified outside of the registry. """ return self.__item_model def item_for_widget(self, widget): # type: (Union[str, WidgetDescription]) -> QStandardItem """Return the QStandardItem for the widget. """ if isinstance(widget, str): widget = self.widget(widget) cat = self.category(widget.category or "Unspecified") cat_ind = self.categories().index(cat) cat_item = self.model().item(cat_ind) widget_ind = self.widgets(cat).index(widget) return cat_item.child(widget_ind) def action_for_widget(self, widget): # type: (Union[str, WidgetDescription]) -> QAction """ Return the QAction instance for the widget (can be a string or a WidgetDescription instance). """ item = self.item_for_widget(widget) return item.data(self.WIDGET_ACTION_ROLE) def create_action_for_item(self, item): # type: (QStandardItem) -> QAction """ Create a QAction instance for the widget description item. """ name = item.text() tooltip = item.toolTip() whatsThis = item.whatsThis() icon = item.icon() action = QAction(icon, name, self, toolTip=tooltip, whatsThis=whatsThis, statusTip=name) widget_desc = item.data(self.WIDGET_DESC_ROLE) action.setData(widget_desc) action.setProperty("item", item) return action def _insert_category(self, desc): # type: (CategoryDescription) -> None """ Override to update the item model and emit the signals. """ priority = desc.priority priorities = [c.priority for c, _ in self.registry] insertion_i = bisect.bisect_right(priorities, priority) WidgetRegistry._insert_category(self, desc) cat_item = self._cat_desc_to_std_item(desc) self.__item_model.insertRow(insertion_i, cat_item) self.category_added.emit(desc.name, desc) def _insert_widget(self, category, desc): # type: (CategoryDescription, WidgetDescription) -> None """ Override to update the item model and emit the signals. """ assert isinstance(category, CategoryDescription) categories = self.categories() cat_i = categories.index(category) _, widgets = self._categories_dict[category.name] priorities = [w.priority for w in widgets] insertion_i = bisect.bisect_right(priorities, desc.priority) WidgetRegistry._insert_widget(self, category, desc) cat_item = self.__item_model.item(cat_i) widget_item = self._widget_desc_to_std_item(desc, category) cat_item.insertRow(insertion_i, widget_item) self.widget_added.emit(category.name, desc.name, desc) def _cat_desc_to_std_item(self, desc): # type: (CategoryDescription) -> QStandardItem """ Create a QStandardItem for the category description. """ item = QStandardItem() item.setText(desc.name) if desc.icon: icon = desc.icon else: icon = "icons/default-category.svg" icon = icon_loader.from_description(desc).get(icon) item.setIcon(icon) if desc.background: background = desc.background else: background = DEFAULT_COLOR background = NAMED_COLORS.get(background, background) brush = QBrush(QColor(background)) item.setData(brush, self.BACKGROUND_ROLE) tooltip = desc.description if desc.description else desc.name item.setToolTip(tooltip) item.setFlags(Qt.ItemIsEnabled) item.setData(desc, self.CATEGORY_DESC_ROLE) return item def _widget_desc_to_std_item(self, desc, category): # type: (WidgetDescription, CategoryDescription) -> QStandardItem """ Create a QStandardItem for the widget description. """ item = QStandardItem(desc.name) item.setText(desc.name) if desc.icon: icon = desc.icon else: icon = "icons/default-widget.svg" icon = icon_loader.from_description(desc).get(icon) item.setIcon(icon) # This should be inherited from the category. background = None if desc.background: background = desc.background elif category.background: background = category.background else: background = DEFAULT_COLOR if background is not None: background = NAMED_COLORS.get(background, background) brush = QBrush(QColor(background)) item.setData(brush, self.BACKGROUND_ROLE) tooltip = tooltip_helper(desc) style = "ul { margin-top: 1px; margin-bottom: 1px; }" tooltip = TOOLTIP_TEMPLATE.format(style=style, tooltip=tooltip) item.setToolTip(tooltip) item.setWhatsThis(whats_this_helper(desc)) item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) item.setData(desc, self.WIDGET_DESC_ROLE) # Create the action for the widget_item action = self.create_action_for_item(item) item.setData(action, self.WIDGET_ACTION_ROLE) return item
def textInteractionFlags(self): # type: () -> Qt.TextInteractionFlags return Qt.TextInteractionFlags(self.__textInteractionFlags)
def update(self, zoom_only=False): self.update_ticks() line_color = self.plot.color(OWPalette.Axis) text_color = self.plot.color(OWPalette.Text) if not self.graph_line or not self.scene(): return self.line_item.setLine(self.graph_line) self.line_item.setPen(line_color) if self.title: self.title_item.setHtml('<b>' + self.title + '</b>') self.title_item.setDefaultTextColor(text_color) if self.title_location == AxisMiddle: title_p = 0.5 elif self.title_location == AxisEnd: title_p = 0.95 else: title_p = 0.05 title_pos = self.graph_line.pointAt(title_p) v = self.graph_line.normalVector().unitVector() dense_text = False if hasattr(self, 'title_margin'): offset = self.title_margin elif self._ticks: if self.should_be_expanded(): offset = 55 dense_text = True else: offset = 35 else: offset = 10 if self.title_above: title_pos += (v.p2() - v.p1()) * (offset + QFontMetrics(self.title_item.font()).height()) else: title_pos -= (v.p2() - v.p1()) * offset ## TODO: Move it according to self.label_pos self.title_item.setVisible(self.show_title) self.title_item.setRotation(-self.graph_line.angle()) c = self.title_item.mapToParent(self.title_item.boundingRect().center()) tl = self.title_item.mapToParent(self.title_item.boundingRect().topLeft()) self.title_item.setPos(title_pos - c + tl) ## Arrows if not zoom_only: if self.start_arrow_item: self.scene().removeItem(self.start_arrow_item) self.start_arrow_item = None if self.end_arrow_item: self.scene().removeItem(self.end_arrow_item) self.end_arrow_item = None if self.arrows & AxisStart: if not zoom_only or not self.start_arrow_item: self.start_arrow_item = QGraphicsPathItem(self.arrow_path, self) self.start_arrow_item.setPos(self.graph_line.p1()) self.start_arrow_item.setRotation(-self.graph_line.angle() + 180) self.start_arrow_item.setBrush(line_color) self.start_arrow_item.setPen(line_color) if self.arrows & AxisEnd: if not zoom_only or not self.end_arrow_item: self.end_arrow_item = QGraphicsPathItem(self.arrow_path, self) self.end_arrow_item.setPos(self.graph_line.p2()) self.end_arrow_item.setRotation(-self.graph_line.angle()) self.end_arrow_item.setBrush(line_color) self.end_arrow_item.setPen(line_color) ## Labels n = len(self._ticks) resize_plot_item_list(self.label_items, n, QGraphicsTextItem, self) resize_plot_item_list(self.label_bg_items, n, QGraphicsRectItem, self) resize_plot_item_list(self.tick_items, n, QGraphicsLineItem, self) test_rect = QRectF(self.graph_line.p1(), self.graph_line.p2()).normalized() test_rect.adjust(-1, -1, 1, 1) n_v = self.graph_line.normalVector().unitVector() if self.title_above: n_p = n_v.p2() - n_v.p1() else: n_p = n_v.p1() - n_v.p2() l_v = self.graph_line.unitVector() l_p = l_v.p2() - l_v.p1() for i in range(n): pos, text, size, step = self._ticks[i] hs = 0.5 * step tick_pos = self.map_to_graph(pos) if not test_rect.contains(tick_pos): self.tick_items[i].setVisible(False) self.label_items[i].setVisible(False) continue item = self.label_items[i] item.setVisible(True) if not zoom_only: if self.id in XAxes or getattr(self, 'is_horizontal', False): item.setHtml('<center>' + Qt.escape(text.strip()) + '</center>') else: item.setHtml(Qt.escape(text.strip())) item.setTextWidth(-1) text_angle = 0 if dense_text: w = min(item.boundingRect().width(), self.max_text_width) item.setTextWidth(w) if self.title_above: label_pos = tick_pos + n_p * (w + self.text_margin) + l_p * item.boundingRect().height() / 2 else: label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect().height() / 2 text_angle = -90 if self.title_above else 90 else: w = min(item.boundingRect().width(), QLineF(self.map_to_graph(pos - hs), self.map_to_graph(pos + hs)).length()) label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect().height() / 2 item.setTextWidth(w) if not self.always_horizontal_text: if self.title_above: item.setRotation(-self.graph_line.angle() - text_angle) else: item.setRotation(self.graph_line.angle() - text_angle) item.setPos(label_pos) item.setDefaultTextColor(text_color) self.label_bg_items[i].setRect(item.boundingRect()) self.label_bg_items[i].setPen(QPen(Qt.NoPen)) self.label_bg_items[i].setBrush(self.plot.color(OWPalette.Canvas)) item = self.tick_items[i] item.setVisible(True) tick_line = QLineF(v) tick_line.translate(-tick_line.p1()) tick_line.setLength(size) if self.title_above: tick_line.setAngle(tick_line.angle() + 180) item.setLine(tick_line) item.setPen(line_color) item.setPos(self.map_to_graph(pos))
def setTextInteractionFlags(self, flags): # type: (Union[Qt.TextInteractionFlags, Qt.TextInteractionFlag]) -> None self.__textInteractionFlags = Qt.TextInteractionFlags(flags)
def update(self, zoom_only=False): self.update_ticks() line_color = self.plot.color(OWPalette.Axis) text_color = self.plot.color(OWPalette.Text) if not self.graph_line or not self.scene(): return self.line_item.setLine(self.graph_line) self.line_item.setPen(line_color) if self.title: self.title_item.setHtml('<b>' + self.title + '</b>') self.title_item.setDefaultTextColor(text_color) if self.title_location == AxisMiddle: title_p = 0.5 elif self.title_location == AxisEnd: title_p = 0.95 else: title_p = 0.05 title_pos = self.graph_line.pointAt(title_p) v = self.graph_line.normalVector().unitVector() dense_text = False if hasattr(self, 'title_margin'): offset = self.title_margin elif self._ticks: if self.should_be_expanded(): offset = 55 dense_text = True else: offset = 35 else: offset = 10 if self.title_above: title_pos += (v.p2() - v.p1()) * ( offset + QFontMetrics(self.title_item.font()).height()) else: title_pos -= (v.p2() - v.p1()) * offset ## TODO: Move it according to self.label_pos self.title_item.setVisible(self.show_title) self.title_item.setRotation(-self.graph_line.angle()) c = self.title_item.mapToParent( self.title_item.boundingRect().center()) tl = self.title_item.mapToParent( self.title_item.boundingRect().topLeft()) self.title_item.setPos(title_pos - c + tl) ## Arrows if not zoom_only: if self.start_arrow_item: self.scene().removeItem(self.start_arrow_item) self.start_arrow_item = None if self.end_arrow_item: self.scene().removeItem(self.end_arrow_item) self.end_arrow_item = None if self.arrows & AxisStart: if not zoom_only or not self.start_arrow_item: self.start_arrow_item = QGraphicsPathItem( self.arrow_path, self) self.start_arrow_item.setPos(self.graph_line.p1()) self.start_arrow_item.setRotation(-self.graph_line.angle() + 180) self.start_arrow_item.setBrush(line_color) self.start_arrow_item.setPen(line_color) if self.arrows & AxisEnd: if not zoom_only or not self.end_arrow_item: self.end_arrow_item = QGraphicsPathItem(self.arrow_path, self) self.end_arrow_item.setPos(self.graph_line.p2()) self.end_arrow_item.setRotation(-self.graph_line.angle()) self.end_arrow_item.setBrush(line_color) self.end_arrow_item.setPen(line_color) ## Labels n = len(self._ticks) resize_plot_item_list(self.label_items, n, QGraphicsTextItem, self) resize_plot_item_list(self.label_bg_items, n, QGraphicsRectItem, self) resize_plot_item_list(self.tick_items, n, QGraphicsLineItem, self) test_rect = QRectF(self.graph_line.p1(), self.graph_line.p2()).normalized() test_rect.adjust(-1, -1, 1, 1) n_v = self.graph_line.normalVector().unitVector() if self.title_above: n_p = n_v.p2() - n_v.p1() else: n_p = n_v.p1() - n_v.p2() l_v = self.graph_line.unitVector() l_p = l_v.p2() - l_v.p1() for i in range(n): pos, text, size, step = self._ticks[i] hs = 0.5 * step tick_pos = self.map_to_graph(pos) if not test_rect.contains(tick_pos): self.tick_items[i].setVisible(False) self.label_items[i].setVisible(False) continue item = self.label_items[i] item.setVisible(True) if not zoom_only: if self.id in XAxes or getattr(self, 'is_horizontal', False): item.setHtml('<center>' + Qt.escape(text.strip()) + '</center>') else: item.setHtml(Qt.escape(text.strip())) item.setTextWidth(-1) text_angle = 0 if dense_text: w = min(item.boundingRect().width(), self.max_text_width) item.setTextWidth(w) if self.title_above: label_pos = tick_pos + n_p * ( w + self.text_margin ) + l_p * item.boundingRect().height() / 2 else: label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect( ).height() / 2 text_angle = -90 if self.title_above else 90 else: w = min( item.boundingRect().width(), QLineF(self.map_to_graph(pos - hs), self.map_to_graph(pos + hs)).length()) label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect( ).height() / 2 item.setTextWidth(w) if not self.always_horizontal_text: if self.title_above: item.setRotation(-self.graph_line.angle() - text_angle) else: item.setRotation(self.graph_line.angle() - text_angle) item.setPos(label_pos) item.setDefaultTextColor(text_color) self.label_bg_items[i].setRect(item.boundingRect()) self.label_bg_items[i].setPen(QPen(Qt.NoPen)) self.label_bg_items[i].setBrush(self.plot.color(OWPalette.Canvas)) item = self.tick_items[i] item.setVisible(True) tick_line = QLineF(v) tick_line.translate(-tick_line.p1()) tick_line.setLength(size) if self.title_above: tick_line.setAngle(tick_line.angle() + 180) item.setLine(tick_line) item.setPen(line_color) item.setPos(self.map_to_graph(pos))
def __missing__(self, key: int) -> Qt.Alignment: a = Qt.Alignment(key) self.setdefault(key, a) return a
def alignment(self): # type: () -> Qt.Alignment """Return text alignment.""" return Qt.Alignment(self.__alignment)
def initStyleOptionForIndex(self, option: QStyleOptionHeader, logicalIndex: int) -> None: """ Similar to initStyleOptionForIndex in Qt 6.0 with the difference that `isSectionSelected` is not used, only `sectionIntersectsSelection` is used (isSectionSelected will scan the entire model column/row when the whole column/row is selected). """ hover = self.logicalIndexAt(self.mapFromGlobal(QCursor.pos())) pressed = self.__pressed if self.highlightSections(): is_selected = self.__sectionIntersectsSelection else: is_selected = lambda _: False state = QStyle.State_None if self.isEnabled(): state |= QStyle.State_Enabled if self.window().isActiveWindow(): state |= QStyle.State_Active if self.sectionsClickable(): if logicalIndex == hover: state |= QStyle.State_MouseOver if logicalIndex == pressed: state |= QStyle.State_Sunken if self.highlightSections(): if is_selected(logicalIndex): state |= QStyle.State_On if self.isSortIndicatorShown() and \ self.sortIndicatorSection() == logicalIndex: option.sortIndicator = ( QStyleOptionHeader.SortDown if self.sortIndicatorOrder() == Qt.AscendingOrder else QStyleOptionHeader.SortUp) style = self.style() model = self.model() orientation = self.orientation() textAlignment = model.headerData(logicalIndex, self.orientation(), Qt.TextAlignmentRole) defaultAlignment = self.defaultAlignment() textAlignment = (textAlignment if isinstance(textAlignment, int) else defaultAlignment) option.section = logicalIndex option.state = QStyle.State(int(option.state) | int(state)) option.textAlignment = Qt.Alignment(int(textAlignment)) option.iconAlignment = Qt.AlignVCenter text = model.headerData(logicalIndex, self.orientation(), Qt.DisplayRole) text = str(text) if text is not None else "" option.text = text icon = model.headerData(logicalIndex, self.orientation(), Qt.DecorationRole) try: option.icon = QIcon(icon) except (TypeError, ValueError): # pragma: no cover pass margin = 2 * style.pixelMetric(QStyle.PM_HeaderMargin, None, self) headerArrowAlignment = style.styleHint(QStyle.SH_Header_ArrowAlignment, None, self) isHeaderArrowOnTheSide = headerArrowAlignment & Qt.AlignVCenter if self.isSortIndicatorShown() and \ self.sortIndicatorSection() == logicalIndex \ and isHeaderArrowOnTheSide: margin += style.pixelMetric(QStyle.PM_HeaderMarkSize, None, self) if not option.icon.isNull(): margin += style.pixelMetric(QStyle.PM_SmallIconSize, None, self) margin += style.pixelMetric(QStyle.PM_HeaderMargin, None, self) if self.textElideMode() != Qt.ElideNone: elideMode = self.textElideMode() if hasattr(option, 'textElideMode'): # Qt 6.0 option.textElideMode = elideMode # pragma: no cover else: option.text = option.fontMetrics.elidedText( option.text, elideMode, option.rect.width() - margin) foregroundBrush = model.headerData(logicalIndex, orientation, Qt.ForegroundRole) try: foregroundBrush = QBrush(foregroundBrush) except (TypeError, ValueError): pass else: option.palette.setBrush(QPalette.ButtonText, foregroundBrush) backgroundBrush = model.headerData(logicalIndex, orientation, Qt.BackgroundRole) try: backgroundBrush = QBrush(backgroundBrush) except (TypeError, ValueError): pass else: option.palette.setBrush(QPalette.Button, backgroundBrush) option.palette.setBrush(QPalette.Window, backgroundBrush) # the section position visual = self.visualIndex(logicalIndex) assert visual != -1 first = self.__isFirstVisibleSection(visual) last = self.__isLastVisibleSection(visual) if first and last: option.position = QStyleOptionHeader.OnlyOneSection elif first: option.position = QStyleOptionHeader.Beginning elif last: option.position = QStyleOptionHeader.End else: option.position = QStyleOptionHeader.Middle option.orientation = orientation # the selected position (in QHeaderView this is always computed even if # highlightSections is False). if self.highlightSections(): previousSelected = is_selected(self.logicalIndex(visual - 1)) nextSelected = is_selected(self.logicalIndex(visual + 1)) else: previousSelected = nextSelected = False if previousSelected and nextSelected: option.selectedPosition = QStyleOptionHeader.NextAndPreviousAreSelected elif previousSelected: option.selectedPosition = QStyleOptionHeader.PreviousIsSelected elif nextSelected: option.selectedPosition = QStyleOptionHeader.NextIsSelected else: option.selectedPosition = QStyleOptionHeader.NotAdjacent