def setZoom(self, zoom): '''Set a new zoom level. Args: zoom (int): The new zoom level. ''' self._zoom = zoom zoomSelector = clip(zoom, self._minZoomScale, self._maxZoomScale) meters = self._scaleView[zoomSelector] if meters >= 1000: self._text = '%d km' % (meters / 1000.0) else: self._text = '%d m' % meters self._meters = meters self._meterPerPixelsEquator = self.EarthCircumference / np.power(2.0, zoom + 8) # Evaluate the bounding box of the current text anchor = self._anchor textRect = QFontMetrics(QFont()).boundingRect(self._text) if anchor == Qt.BottomRightCorner or anchor == Qt.TopRightCorner: textRect.moveLeft(-textRect.width() - 10) textRect.moveTop(-textRect.height() + 10) else: textRect.moveTop(-textRect.height() + 14) self._textRect = QRectF(textRect) self.update()
def setZoom(self, zoom): '''Set a new zoom level. Args: zoom (int): The new zoom level. ''' self._zoom = zoom zoomSelector = clip(zoom, self._minZoomScale, self._maxZoomScale) meters = self._scaleView[zoomSelector] if meters >= 1000: self._text = '%d km' % (meters / 1000.0) else: self._text = '%d m' % meters self._meters = meters self._meterPerPixelsEquator = self.EarthCircumference / np.power( 2.0, zoom + 8) # Evaluate the bounding box of the current text anchor = self._anchor textRect = QFontMetrics(QFont()).boundingRect(self._text) if anchor == Qt.BottomRightCorner or anchor == Qt.TopRightCorner: textRect.moveLeft(-textRect.width() - 10) textRect.moveTop(-textRect.height() + 10) else: textRect.moveTop(-textRect.height() + 14) self._textRect = QRectF(textRect) self.update()
def sizeHint(self): """Override Qt method. Returns the widget size hint (based on the editor font size). """ fm = QFontMetrics(self.editor.font()) size_hint = QSize(fm.height(), fm.height()) if size_hint.width() > 16: size_hint.setWidth(16) return size_hint
def _resize_widgets(build_popup, visible): output = build_popup.textBrowser_output cmd = build_popup.lineEdit_cmd font = QFont('Monospace' if visible else '') font.setStyleHint(QFont.TypeWriter) metrics = QFontMetrics(font) output.setFont(font) mw = build_popup.min_width = metrics.width(' ' * 80) output.setMinimumSize(QSize(mw, 20*metrics.height())) cmd.setMinimumSize(QSize(metrics.width(' ' * 40), metrics.height()))
def set(self, U=None, vmin=None, vmax=None): # normalize U fm = QFontMetrics(self.font()) self.vmin = vmin if vmin is not None else ( np.min(U) if U is not None else 0.) self.vmax = vmax if vmax is not None else ( np.max(U) if U is not None else 1.) difference = abs(self.vmin - self.vmax) if difference == 0: precision = 3 else: precision = m.log( max(abs(self.vmin), abs(self.vmax)) / difference, 10) + 1 precision = int(min(max(precision, 3), 8)) self.vmin_str = format( ('{:.' + str(precision) + '}').format(self.vmin)) self.vmax_str = format( ('{:.' + str(precision) + '}').format(self.vmax)) self.vmin_width = fm.width(self.vmin_str) self.vmax_width = fm.width(self.vmax_str) self.text_height = fm.height() * 1.5 self.text_ascent = fm.ascent() * 1.5 self.text_descent = fm.descent() * 1.5 self.setMinimumSize( max(self.vmin_width, self.vmax_width) + 20, 300) self.update()
def _get_mouse_position(self): # Creates a text with empty space to get the height of the rendered text - this is used # to provide the same offset for the tooltip, scaled relative to the current resolution and zoom. font_metrics = QFontMetrics(QFont(" ")) # The height itself is divided by 2 just to reduce the offset so that the tooltip is # reasonably positioned, relative to the cursor return QCursor.pos() + QPoint(font_metrics.height() / 2, 0)
def create_layout(self): ''' Creates the complete layout including all controls ''' self.title_label = ElidingLabel(text=self.dock_widget.windowTitle()) self.title_label.set_elide_mode(Qt.ElideRight) self.title_label.setObjectName("dockWidgetTabLabel") self.title_label.setAlignment(Qt.AlignCenter) self.close_button = QPushButton() self.close_button.setObjectName("tabCloseButton") set_button_icon(self.public.style(), self.close_button, QStyle.SP_TitleBarCloseButton) self.close_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.close_button.setVisible(False) self.close_button.setToolTip("Close Tab") self.close_button.clicked.connect(self.public.close_requested) fm = QFontMetrics(self.title_label.font()) spacing = round(fm.height() / 4.0) # Fill the layout layout = QBoxLayout(QBoxLayout.LeftToRight) layout.setContentsMargins(2 * spacing, 0, 0, 0) layout.setSpacing(0) self.public.setLayout(layout) layout.addWidget(self.title_label, 1) layout.addSpacing(spacing) layout.addWidget(self.close_button) layout.addSpacing(round(spacing * 4.0 / 3.0)) layout.setAlignment(Qt.AlignCenter) self.title_label.setVisible(True)
def show_mouse_toast(message): # Creates a text with empty space to get the height of the rendered text - this is used # to provide the same offset for the tooltip, scaled relative to the current resolution and zoom. font_metrics = QFontMetrics(QFont(" ")) # The height itself is divided by 2 just to reduce the offset so that the tooltip is # reasonably position relative to the cursor QToolTip.showText(QCursor.pos() + QPoint(font_metrics.height() / 2, 0), message)
def __init__(self, parent, widthSpace=10): super().__init__() self.parent = parent self.widthSpace = widthSpace self.dateFmt = "%d %b %Y" self.headerLabels = [ 'Date', 'Time', 'Distance (km)', 'Avg. speed\n(km/h)', 'Calories', 'Gear' ] self.setHeaderLabels(self.headerLabels) self.header().setStretchLastSection(False) # make header tall enough for two rows of text (avg speed has line break) font = self.header().font() metrics = QFontMetrics(font) height = metrics.height() self.header().setMinimumHeight(height * 2) # align header text centrally for idx in range(len(self.headerLabels)): self.headerItem().setTextAlignment(idx, Qt.AlignCenter) self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.makeTree() self.sortColumn = None self.sortDescending = [True for _ in range(len(self.headerLabels))] self.header().setSectionsClickable(True) self.header().sectionClicked.connect(self.sortTree) self.currentItemChanged.connect(self._itemChanged) self.itemSelectionChanged.connect(self._summariseSelected) self.sortTree(0) msg = "Browse all sessions, grouped by month. Click on the headers \n" msg += "to sort by that metric in ascending or descending order.\n" msg += "Click on a session to highlight it in the plot." self.setToolTip(msg) self.editAction = QAction("Edit") self.editAction.setShortcut(QKeySequence("Ctrl+E")) self.editAction.triggered.connect(self._editItems) self.addAction(self.editAction) self.mergeAction = QAction("Merge") self.mergeAction.setShortcut(QKeySequence("Ctrl+M")) self.mergeAction.triggered.connect(self.combineRows) self.addAction(self.mergeAction) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self._showContextMenu)
def resize_to_content(self, lines): if len(lines) == 0: lines.append('') # resize properly fm = QFontMetrics(self.font()) text_width = fm.width(lines[0]+'__') text_width = text_width+20 # some buffer text_height = fm.height()*(len(lines))+12 # also some vertical buffer self.setFixedWidth(text_width if text_width > self.base_width else self.base_width) self.setFixedHeight(text_height if text_height > self.base_height else self.base_height) self.parent_node_instance.update_shape()
def __init__(self, colormap: BaseColormap, accepted: bool, name: str, removable: bool = False, used: bool = False): super().__init__() self.image = convert_colormap_to_image(colormap) self.name = name self.removable = removable self.checked = QCheckBox() self.checked.setChecked(accepted) self.checked.setDisabled(used) self.setMinimumWidth(80) metrics = QFontMetrics(QFont()) layout = QHBoxLayout() layout.addWidget(self.checked) layout.addStretch(1) self.remove_btn = QToolButton() self.remove_btn.setIcon(_icon_selector.close_icon) if removable: self.remove_btn.setToolTip("Remove colormap") else: self.remove_btn.setToolTip("This colormap is protected") self.remove_btn.setEnabled(not accepted and self.removable) self.edit_btn = QToolButton() self.edit_btn.setIcon(_icon_selector.edit_icon) layout.addWidget(self.remove_btn) layout.addWidget(self.edit_btn) self.setLayout(layout) self.checked.stateChanged.connect(self._selection_changed) self.edit_btn.clicked.connect(partial(self.edit_request.emit, name)) if isinstance(colormap, ColorMap): self.edit_btn.clicked.connect( partial(self.edit_request[ColorMap].emit, colormap)) self.edit_btn.setToolTip("Create colormap base on this") else: self.edit_btn.setDisabled(True) self.edit_btn.setToolTip("This colormap is not editable") self.remove_btn.clicked.connect(partial(self.remove_request.emit, name)) self.setMinimumHeight( max(metrics.height(), self.edit_btn.minimumHeight(), self.checked.minimumHeight()) + 20)
def __init__(self, parent, ancestor): QListWidget.__init__(self, ancestor) self.setWindowFlags(Qt.SubWindow | Qt.FramelessWindowHint) self.textedit = parent self.hide() self.itemActivated.connect(self.item_selected) self.currentRowChanged.connect(self.row_changed) self.is_internal_console = False self.completion_list = None self.completion_position = None self.automatic = False # Setup item rendering self.setItemDelegate(HTMLDelegate(self, margin=3)) self.setMinimumWidth(DEFAULT_COMPLETION_ITEM_WIDTH) # Initial item height and width fm = QFontMetrics(self.textedit.font()) self.item_height = fm.height() self.item_width = self.width()
def create_layout(self): ''' Creates the complete layout including all controls ''' self.title_label = ElidingLabel() self.title_label.set_elide_mode(Qt.ElideRight) self.title_label.setText("DockWidget->windowTitle()") self.title_label.setObjectName("floatingTitleLabel") self.title_label.setAlignment(Qt.AlignLeft) self.close_button = QPushButton() self.close_button.setObjectName("floatingTitleCloseButton") self.close_button.setFlat(True) # self.close_button.setAutoRaise(True) set_button_icon(self.public.style(), self.close_button, QStyle.SP_TitleBarCloseButton) self.close_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.close_button.setVisible(True) self.close_button.setFocusPolicy(Qt.NoFocus) self.close_button.clicked.connect(self.public.close_requested) fm = QFontMetrics(self.title_label.font()) spacing = round(fm.height() / 4.0) # Fill the layout layout = QBoxLayout(QBoxLayout.LeftToRight) layout.setContentsMargins(6, 0, 0, 0) layout.setSpacing(0) self.public.setLayout(layout) layout.addWidget(self.title_label, 1) layout.addSpacing(spacing) layout.addWidget(self.close_button) layout.setAlignment(Qt.AlignCenter) self.title_label.setVisible(True)
def __init__(self, mainWindow, parent=None, rows=5): self.headerLabels = [ 'Date', 'Time', 'Distance (km)', 'Avg. speed\n(km/h)', 'Calories', 'Gear' ] columns = len(self.headerLabels) super().__init__(rows, columns) self.mainWindow = mainWindow self.parent = parent # dict of columns that can be selected and the functions used to compare values self.selectableColumns = { 'Time': hourMinSecToFloat, 'Distance (km)': float, 'Avg. speed (km/h)': float, 'Calories': float } self.setHorizontalHeaderLabels(self.headerLabels) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.selectKey = "Avg. speed (km/h)" # make header tall enough for two rows of text (avg speed has line break) font = self.header.font() metrics = QFontMetrics(font) height = metrics.height() self.header.setMinimumHeight(height * 2) self.currentCellChanged.connect(self._cellChanged) self.header.sectionClicked.connect(self.selectColumn) self.selectColumn(self.headerLabels.index('Avg. speed\n(km/h)')) self.newIdx = None self._setToolTip(rows)
class LineNumberArea(QWidget): def __init__(self, textEditor): QWidget.__init__(self, textEditor) self.textEditor = textEditor self.prefix_color = Qt.lightGray self.prefix_font_color = Qt.black self.update_font() self._firstlinecode = [' >>> '] self.blinks = [':', ' '] self.painter = QPainter() self.promptTimer = QTimer(self) self.promptTimer.timeout.connect(self.nextPrompt) self.profile_start = time.monotonic() self.profile_threshold = 10 self.profile_enabled = False def start_profiling(self): self.profile_start = time.monotonic() self.profile_enabled = True def stop_profiling(self): self.profile_enabled = False def update_font(self): self.setFont(self.textEditor.font()) self.fontmetric = QFontMetrics(self.textEditor.font()) self.prefixwidth = self.fontmetric.width('12345') self.prefixheight = self.fontmetric.height() self.setFixedWidth(self.prefixwidth) self.textEditor.setViewportMargins(self.prefixwidth, 0, 0, 0) def set_firstlinecode(self, prefices): self._firstlinecode = prefices if len(self._firstlinecode) > 1: self.promptTimer.start(500) else: self.promptTimer.stop() def get_firstlinecode(self): if self.profile_enabled: elapsed = time.monotonic() - self.profile_start if elapsed > self.profile_threshold: if elapsed >= 3600: profile = time.strftime("%H:%M", time.gmtime(elapsed)) else: profile = time.strftime("%M:%S", time.gmtime(elapsed)) return profile[:-3] + self.blinks[0] + profile[-2:] return self._firstlinecode[0] firstlinecode = property(get_firstlinecode, set_firstlinecode) def nextPrompt(self): self._firstlinecode = self._firstlinecode[1:] + self._firstlinecode[:1] self.blinks = self.blinks[1:] + self.blinks[:1] self.repaint() def paintEvent(self, event): cursor = self.textEditor.cursorForPosition( self.textEditor.viewport().pos()) painter = self.painter try: painter.begin(self) painter.fillRect(event.rect(), self.prefix_color) for i in range(100): blockNumber = cursor.block().blockNumber() if blockNumber == 0: code = self.firstlinecode else: code = str(blockNumber + 1) painter.setPen(self.prefix_font_color) y = self.textEditor.cursorRect( cursor).y() + self.textEditor.viewport().pos().y() - 2 painter.drawText(0, y, self.prefixwidth, self.prefixheight, Qt.AlignRight, code) if y > event.rect().bottom(): break if not cursor.block().next().isValid(): break cursor.movePosition(cursor.NextBlock) finally: painter.end() def sizeHint(self): return QSize(self.prefixwidth, 0)
def minimumSizeHint(self): fm = QFontMetrics(self.font()) return QSize(fm.height(), fm.height())
class NodeGeometry: def __init__(self, node: NodeBase): super().__init__() self._node = node self._model = node.model self._dragging_pos = QPointF(-1000, -1000) self._entry_width = 0 self._entry_height = 20 self._font_metrics = QFontMetrics(QFont()) self._height = 150 self._hovered = False self._input_port_width = 70 self._output_port_width = 70 self._spacing = 20 self._style = node.style self._width = 100 f = QFont() f.setBold(True) self._bold_font_metrics = QFontMetrics(f) @property def height(self) -> int: """ Height Returns ------- value : int """ return self._height @height.setter def height(self, h: int): self._height = int(h) @property def width(self) -> int: """ Width Returns ------- value : int """ return self._width @width.setter def width(self, width: int): """ Set width Parameters ---------- width : int """ self._width = int(width) @property def entry_height(self) -> int: """ Entry height Returns ------- value : int """ return self._entry_height @entry_height.setter def entry_height(self, h: int): """ Set entry height Parameters ---------- h : int """ self._entry_height = int(h) @property def entry_width(self) -> int: """ Entry width Returns ------- value : int """ return self._entry_width @entry_width.setter def entry_width(self, width: int): """ Set entry width Parameters ---------- width : int """ self._entry_width = int(width) @property def spacing(self) -> int: """ Spacing Returns ------- value : int """ return self._spacing @spacing.setter def spacing(self, s: int): """ Set spacing Parameters ---------- s : int """ self._spacing = int(s) @property def hovered(self) -> bool: """ Hovered Returns ------- value : bool """ return self._hovered @hovered.setter def hovered(self, h: int): """ Set hovered Parameters ---------- h : int """ self._hovered = bool(h) @property def num_sources(self) -> int: """ N sources Returns ------- value : int """ return self._model.num_ports[PortType.output] @property def num_sinks(self) -> int: """ N sinks Returns ------- value : int """ return self._model.num_ports[PortType.input] @property def dragging_pos(self) -> QPointF: """ Dragging pos Returns ------- value : QPointF """ return self._dragging_pos @dragging_pos.setter def dragging_position(self, pos: QPointF): self._dragging_pos = QPointF(pos) def entry_bounding_rect(self, *, addon=0.0) -> QRectF: """ Entry bounding rect Returns ------- value : QRectF """ return QRectF(0 - addon, 0 - addon, self._entry_width + 2 * addon, self._entry_height + 2 * addon) @property def bounding_rect(self) -> QRectF: """ Bounding rect Returns ------- value : QRectF """ addon = 4 * self._style.connection_point_diameter return QRectF(0 - addon, 0 - addon, self._width + 2 * addon, self._height + 2 * addon) def recalculate_size(self, font: QFont = None): """ If font is unspecified, Updates size unconditionally Otherwise, Updates size if the QFontMetrics is changed """ if font is not None: font_metrics = QFontMetrics(font) bold_font = QFont(font) bold_font.setBold(True) bold_font_metrics = QFontMetrics(bold_font) if self._bold_font_metrics == bold_font_metrics: return self._font_metrics = font_metrics self._bold_font_metrics = bold_font_metrics self._entry_height = self._font_metrics.height() max_num_of_entries = max((self.num_sinks, self.num_sources)) step = self._entry_height + self._spacing height = step * max_num_of_entries widget = self._model.embedded_widget() if widget: height = max((height, widget.height())) height += self.caption_height self._input_port_width = self.port_width(PortType.input) self._output_port_width = self.port_width(PortType.output) width = self._input_port_width + self._output_port_width + 2 * self._spacing if widget: width += widget.width() width = max((width, self.caption_width)) if self._model.validation_state() != NodeValidationState.valid: width = max((width, self.validation_width)) height += self.validation_height + self._spacing self._width = width self._height = height def port_scene_position(self, port_type: PortType, index: int, t: QTransform = None) -> QPointF: """ Port scene position Parameters ---------- port_type : PortType index : int t : QTransform Returns ------- value : QPointF """ if t is None: t = QTransform() step = self._entry_height + self._spacing total_height = float(self.caption_height) + step * index # TODO_UPSTREAM: why? total_height += step / 2.0 if port_type == PortType.output: x = self._width + self._style.connection_point_diameter result = QPointF(x, total_height) elif port_type == PortType.input: x = -float(self._style.connection_point_diameter) result = QPointF(x, total_height) else: raise ValueError(port_type) return t.map(result) def check_hit_scene_point(self, port_type: PortType, scene_point: QPointF, scene_transform: QTransform) -> Port: """ Check hit scene point Parameters ---------- port_type : PortType scene_point : QPointF scene_transform : QTransform Returns ------- value : Port """ if port_type == PortType.none: return None tolerance = 2.0 * self._style.connection_point_diameter for idx, port in self._node.state[port_type].items(): pos = port.get_mapped_scene_position(scene_transform) - scene_point distance = math.sqrt(QPointF.dotProduct(pos, pos)) if distance < tolerance: return port @property def resize_rect(self) -> QRect: """ Resize rect Returns ------- value : QRect """ rect_size = 7 return QRect(self._width - rect_size, self._height - rect_size, rect_size, rect_size) @property def widget_position(self) -> QPointF: """ Returns the position of a widget on the Node surface Returns ------- value : QPointF """ widget = self._model.embedded_widget() if not widget: return QPointF() if widget.sizePolicy().verticalPolicy() & QSizePolicy.ExpandFlag: # If the widget wants to use as much vertical space as possible, # place it immediately after the caption. return QPointF(self._spacing + self.port_width(PortType.input), self.caption_height) if self._model.validation_state() != NodeValidationState.valid: return QPointF( self._spacing + self.port_width(PortType.input), (self.caption_height + self._height - self.validation_height - self._spacing - widget.height()) / 2.0, ) return QPointF(self._spacing + self.port_width(PortType.input), (self.caption_height + self._height - widget.height()) / 2.0) def equivalent_widget_height(self) -> int: ''' The maximum height a widget can be without causing the node to grow. Returns ------- value : int ''' base_height = self.height - self.caption_height if self._model.validation_state() != NodeValidationState.valid: return (base_height + self.validation_height) return base_height @property def validation_height(self) -> int: """ Validation height Returns ------- value : int """ msg = self._model.validation_message() return self._bold_font_metrics.boundingRect(msg).height() @property def validation_width(self) -> int: """ Validation width Returns ------- value : int """ msg = self._model.validation_message() return self._bold_font_metrics.boundingRect(msg).width() @staticmethod def calculate_node_position_between_node_ports( target_port_index: int, target_port: PortType, target_node: NodeBase, source_port_index: int, source_port: PortType, source_node: NodeBase, new_node: NodeBase) -> QPointF: """ calculate node position between node ports Calculating the nodes position in the scene. It'll be positioned half way between the two ports that it "connects". The first line calculates the halfway point between the ports (node position + port position on the node for both nodes averaged). The second line offsets self coordinate with the size of the new node, so that the new nodes center falls on the originally calculated coordinate, instead of it's upper left corner. Parameters ---------- target_port_index : int target_port : PortType target_node : Node source_port_index : int source_port : PortType source_node : Node new_node : Node Returns ------- value : QPointF """ converter_node_pos = (source_node.graphics_object.pos() + source_node.geometry.port_scene_position( source_port, source_port_index) + target_node.graphics_object.pos() + target_node.geometry.port_scene_position( target_port, target_port_index)) / 2.0 converter_node_pos.setX(converter_node_pos.x() - new_node.geometry.width / 2.0) converter_node_pos.setY(converter_node_pos.y() - new_node.geometry.height / 2.0) return converter_node_pos @property def caption_height(self) -> int: """ Caption height Returns ------- value : int """ if not self._model.caption_visible: return 0 name = self._model.caption return self._bold_font_metrics.boundingRect(name).height() @property def caption_width(self) -> int: """ Caption width Returns ------- value : int """ if not self._model.caption_visible: return 0 name = self._model.caption return self._bold_font_metrics.boundingRect(name).width() def port_width(self, port_type: PortType) -> int: """ Port width Parameters ---------- port_type : PortType Returns ------- value : int """ names = [port.display_text for port in self._node[port_type].values()] if not names: return 0 return max( self._font_metrics.horizontalAdvance(name) for name in names) @property def size(self): """ Get the node size Parameters ---------- node : Node Returns ------- value : QSizeF """ return QSizeF(self.width, self.height)