def test_polygon(): rect1 = core.Rect(0, 0, 2, 1) rect2 = core.Rect(1, 0, 2, 1) poly = gui.Polygon(rect1, closed=True) poly2 = gui.Polygon(rect2, closed=True) intersect = poly & poly2 expected = gui.Polygon(core.Rect(1, 0, 1, 1), closed=True) # TODO: breaks PySide2 testing # assert intersect == expected # assert intersect.get_points() == [ # core.Point(1, 0), # core.Point(2, 0), # core.Point(2, 1), # core.Point(1, 1), # core.Point(1, 0), # ] union = poly | poly2 expected = gui.Polygon(core.Rect(0, 0, 3, 1), closed=True) assert list(union) == list(expected) sub = union - intersect xor = poly ^ poly2 assert list(sub) == list(xor) # assert sub == xor with open("data.pkl", "wb") as jar: pickle.dump(poly, jar) with open("data.pkl", "rb") as jar: poly = pickle.load(jar) poly.add_points((0, 1), core.Point(2, 2)) assert bool(poly) is True assert core.Point(1, 0) in poly p = core.Point(5, 5) poly[5] = p assert poly[5] == p bytes(poly)
def paintEvent(self, event): painter = gui.Painter(self) painter.use_antialiasing() painter.set_pen("none") painter.set_transparent_background(False) painter.set_transparent_background(True) painter.set_brush(gui.Brush()) with painter.paint_on(widgets.Widget()): pass painter.set_brush(gui.Color("red")) painter.fill_rect((0, 1, 3, 5), "transparent") painter.fill_rect(core.Rect(), "transparent") with pytest.raises(InvalidParamError): painter.fill_rect(core.Rect(), "transparent", "test") with pytest.raises(ValueError): painter.fill_rect(core.Rect(), "testus") painter.set_color("black") painter.set_composition_mode("source_atop") painter.get_composition_mode() == "source_atop" with pytest.raises(InvalidParamError): painter.set_composition_mode("test") painter.set_clip_path(gui.PainterPath(), "replace") with pytest.raises(InvalidParamError): painter.set_clip_path(gui.PainterPath(), "test") with pytest.raises(InvalidParamError): painter.set_pen(style="test") with painter.backup_state(): pass assert painter.get_text_rect("test") is not None
def test_painter(): painter = gui.Painter(gui.Image()) painter.use_antialiasing() painter.set_pen("none") painter.fill_rect((0, 1, 3, 5), "transparent") painter.fill_rect(core.Rect(), "transparent") with pytest.raises(ValueError): painter.fill_rect(core.Rect(), "testus") painter.set_color("black") painter.set_composition_mode("source_atop") with pytest.raises(ValueError): painter.set_composition_mode("test") with pytest.raises(ValueError): painter.set_pen("test")
def resizeEvent(self, event): super().resizeEvent(event) cr = self.contentsRect() rect = core.Rect(cr.left(), cr.top(), self.line_area_width(), cr.height()) self.line_area.setGeometry(rect)
def test_propertyanimation(): animation = core.PropertyAnimation() button = widgets.PushButton() animation.set_easing("in_cubic") assert animation.get_easing() == "in_cubic" animation.set_direction("forward") with pytest.raises(InvalidParamError): animation.set_direction("test") assert animation.get_direction() == "forward" assert animation.get_state() == "stopped" animation.setDuration(100) assert len(animation) == 100 animation.apply_to(button, "geometry") assert animation.get_property_name() == "geometry" animation.setEndValue(core.Rect(20, 50, 70, 89)) animation[0] = 1 assert animation[0] == 1 with pytest.raises(InvalidParamError): animation.start_animation("test") # TODO: this one breaks PySide2 test # animation.start_animation("keep") def test(val): return val animation.set_easing(test) # PySide2 looses custom fn here if prettyqt.qt.API.startswith("pyqt"): assert animation.get_easing() == test else: with pytest.raises(AttributeError): animation.get_easing()
def paintEvent(self, event): painter = gui.Painter(self) painter.fill_rect(self.rect(), "transparent") painter.use_antialiasing() if self._current_counter >= self._line_num: self._current_counter = 0 painter.set_pen("none") for i in range(0, self._line_num): painter.save() painter.translate(self._inner_radius + self._line_length, self._inner_radius + self._line_length) rotate_angle = float(360 * i) / float(self._line_num) painter.rotate(rotate_angle) painter.translate(self._inner_radius, 0) distance = self.linecount_distance_from_primary( i, self._current_counter, self._line_num) color = self.current_line_color(distance, self._line_num, self._trail_fade_percentage, self._minimum_trail_opacity, self._color) painter.setBrush(color) rect = core.Rect(0, -self._line_width / 2, self._line_length, self._line_width) painter.drawRoundedRect(rect, self._roundness, self._roundness, QtCore.Qt.RelativeSize) painter.restore()
def do_layout(self, rect: QtCore.QRect, test_only: bool) -> int: x = rect.x() y = rect.y() line_height = 0 space = self.spacing() pb = widgets.SizePolicy.ControlType.PushButton for item in self.items: wid = item.widget() space_x = space + wid.style().layoutSpacing( pb, pb, constants.HORIZONTAL) next_x = x + item.sizeHint().width() + space_x if next_x - space_x > rect.right() and line_height > 0: x = rect.x() space_y = space + wid.style().layoutSpacing( pb, pb, constants.VERTICAL) y = y + line_height + space_y next_x = x + item.sizeHint().width() + space_x line_height = 0 if not test_only: item.setGeometry(core.Rect(core.Point(x, y), item.sizeHint())) x = next_x line_height = max(line_height, item.sizeHint().height()) return y + line_height - rect.y()
def test_painter(): painter = gui.Painter() painter.use_antialiasing() painter.set_pen("none") painter.fill_rect(core.Rect(), "transparent") painter.set_color("black") painter.set_composition_mode("source_atop")
def pixmap(self, size, mode, state) -> QtGui.QPixmap: pm = QtGui.QPixmap(size) pm.fill(QtCore.Qt.GlobalColor.transparent) # type: ignore rect = core.Rect(ZERO_COORD, size) painter = gui.Painter(pm) self.paint(painter, rect, mode, state) return pm
def do_layout(self, rect, test_only): x = rect.x() y = rect.y() line_height = 0 for item in self.items: wid = item.widget() pb = widgets.SizePolicy.PushButton space = self.spacing() space_x = space + wid.style().layoutSpacing( pb, pb, QtCore.Qt.Horizontal) next_x = x + item.sizeHint().width() + space_x if next_x - space_x > rect.right() and line_height > 0: x = rect.x() space_y = space + wid.style().layoutSpacing( pb, pb, QtCore.Qt.Vertical) y = y + line_height + space_y next_x = x + item.sizeHint().width() + space_x line_height = 0 if not test_only: item.setGeometry(core.Rect(core.Point(x, y), item.sizeHint())) x = next_x line_height = max(line_height, item.sizeHint().height()) return y + line_height - rect.y()
def set_text(self, text: str): self.text = text metrics = QtGui.QFontMetrics(self.font) self.text_rect = metrics.boundingRect(core.Rect(0, 0, 150, 150), QtCore.Qt.AlignLeft, self.text) self.text_rect.translate(5, 5) self.prepareGeometryChange() self.rect = self.text_rect.adjusted(-5, -5, 5, 5)
def paintEvent(self, event): painter = widgets.StylePainter(self) # ticks opt = widgets.StyleOptionSlider() self.initStyleOption(opt) opt.subControls = widgets.Style.SubControl.SC_SliderTickmarks painter.draw_complex_control("slider", opt) # groove opt.sliderPosition = 20 opt.sliderValue = 0 opt.subControls = GROOVE_STYLE painter.draw_complex_control("slider", opt) # handle rects opt.sliderPosition = self.lower_pos lr = self.style().subControlRect(SLIDER_STYLE, opt, HANDLE_STYLE, self) lrv = self.pick(lr.center()) opt.sliderPosition = self.upper_pos ur = self.style().subControlRect(SLIDER_STYLE, opt, HANDLE_STYLE, self) urv = self.pick(ur.center()) # span minv = min(lrv, urv) maxv = max(lrv, urv) c = self.style().subControlRect(SLIDER_STYLE, opt, GROOVE_STYLE, self).center() if self.is_horizontal(): rect = core.Rect(core.Point(minv, c.y() - 2), core.Point(maxv, c.y() + 1)) else: rect = core.Rect(core.Point(c.x() - 2, minv), core.Point(c.x() + 1, maxv)) self._draw_span(painter, rect) # handles if self.last_pressed == "lower": self.draw_handle(painter, "upper") self.draw_handle(painter, "lower") else: self.draw_handle(painter, "lower") self.draw_handle(painter, "upper")
def test_codeeditor(): editor = custom_widgets.CodeEditor() assert editor.text() == "" editor.line_area_width() editor.set_syntax("python") event = QtGui.QResizeEvent(core.Size(10, 10), core.Size(20, 20)) editor.resizeEvent(event) event = QtGui.QPaintEvent(core.Rect(0, 0, 20, 20)) editor.line_area_paintevent(event)
def fill_rect(self, rect, color, pattern="solid"): if isinstance(rect, tuple): rect = core.Rect(*rect) if isinstance(color, str): if color not in gui.Color.colorNames(): raise ValueError("Invalid value for color.") color = gui.Color(color) if pattern != "solid": color = gui.Brush(color, PATTERNS[pattern]) self.fillRect(rect, color)
def paint( self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex, ): """Override to paint an icon based on given Pixmap / Color / Icon. Pixmap / Color / Icon must be set to 'QtCore.Qt.ItemDataRole.UserRole + 1000' Args: painter (QtGui.QPainter): painter to paint the icon option (QtWidgets.QStyleOptionViewItem): state of the item to be displayed index (QtCore.QModelIndex): index which gets decorated """ super().paint(painter, option, index) value = index.data(DecorationRole2) if not value: return margin = 10 mode = gui.Icon.Mode.Normal if not (option.state & widgets.Style.StateFlag.State_Enabled): mode = gui.Icon.Mode.Disabled elif option.state & widgets.Style.StateFlag.State_Selected: mode = gui.Icon.Mode.Selected if isinstance(value, QtGui.QPixmap): icon = QtGui.QIcon(value) option.decorationSize = int(value.size() / value.devicePixelRatio()) elif isinstance(value, QtGui.QColor): pixmap = QtGui.QPixmap(option.decorationSize) pixmap.fill(value) icon = QtGui.QIcon(pixmap) elif isinstance(value, QtGui.QImage): icon = QtGui.QIcon(QtGui.QPixmap.fromImage(value)) option.decorationSize = int(value.size() / value.devicePixelRatio()) elif isinstance(value, QtGui.QIcon): is_on = option.state & widgets.Style.StateFlag.State_Open state = gui.Icon.State.On if is_on else gui.Icon.State.Off actual_size = option.icon.actualSize(option.decorationSize, mode, state) option.decorationSize = option.decorationSize & actual_size r = core.Rect(core.Point(), option.decorationSize) r.moveCenter(option.rect.center()) r.setRight(option.rect.right() - margin) state = (gui.Icon.State.On if option.state & widgets.Style.StateFlag.State_Open else gui.Icon.State.Off) alignment = constants.ALIGN_RIGHT | constants.ALIGN_V_CENTER # type: ignore icon.paint(painter, r, alignment, mode, state)
def _cursor_in_area(self) -> bool: geom = self.geometry() top_left = self.mapToGlobal(geom.topLeft()) bottom_right = self.mapToGlobal(geom.bottomRight()) geom = core.Rect(top_left, bottom_right) geom.setTop(geom.top() - CURSOR_MARGIN_TOP) geom.setBottom(geom.bottom() + CURSOR_MARGIN_BOTTOM) geom.setLeft(geom.left() - CURSOR_MARGIN_LEFT) geom.setRight(geom.right() + CURSOR_MARGIN_RIGHT) cursor_pos = gui.Cursor.get_position() return geom.contains(cursor_pos)
def updateEditorGeometry( self, editor: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex, ): rect = core.Rect(option.rect) min_width = editor.minimumSizeHint().width() if rect.width() < min_width: rect.setWidth(min_width) editor.setGeometry(rect) # create a new mask based on the option rectangle, then apply it mask = gui.Region(0, 0, option.rect.width(), option.rect.height()) editor.setProperty("offMask", mask) # type: ignore editor.setMask(mask)
def test_flowlayout(): widget = widgets.Widget() layout = custom_widgets.FlowLayout(margin=1) btn = widgets.PushButton("Short") layout += btn layout += widgets.PushButton("Longer") layout += widgets.PushButton("Different text") layout += widgets.PushButton("More text") layout += widgets.PushButton("Even longer button text") layout.do_layout(core.Rect(), False) layout.sizeHint() widget.set_layout(layout) assert layout[0] == btn for i in layout: pass assert len(layout) == 5 layout.get_children() with open("data.pkl", "wb") as jar: pickle.dump(layout, jar) with open("data.pkl", "rb") as jar: layout = pickle.load(jar)
def test_spanslider(qtbot): slider = custom_widgets.SpanSlider() qtbot.addWidget(slider) slider.set_lower_value(10) slider.set_upper_value(20) slider.set_lower_pos(15) slider.set_upper_pos(25) assert slider.lower_value == 15 assert slider.upper_value == 25 slider.set_lower_value(12) slider.set_upper_pos(20) color = gui.Color("blue") slider.set_left_color(color) slider.set_right_color(color) slider.swap_controls() slider.trigger_action(slider.SliderNoAction, True) slider.trigger_action(slider.SliderSingleStepAdd, True) slider.paintEvent(None) slider.pixel_pos_to_value(100) slider.draw_span(gui.Painter(), core.Rect()) slider.move_pressed_handle() qtbot.mouseClick(slider, QtCore.Qt.LeftButton) qtbot.mouseMove(slider, core.Point(20, 20)) assert slider.movement_mode == "free"
def get_bounding_rect(self, *args) -> core.Rect: return core.Rect(self.boundingRect(*args))
def get_virtual_geometry(self) -> core.Rect: return core.Rect(self.virtualGeometry())
def paintEvent(self, e): super().paintEvent(e) style = self.sl.style() painter = gui.Painter(self) st_slider = widgets.StyleOptionSlider() st_slider.initFrom(self.sl) st_slider.orientation = self.sl.orientation() length = style.pixelMetric(widgets.Style.PM_SliderLength, st_slider, self.sl) available = style.pixelMetric(widgets.Style.PM_SliderSpaceAvailable, st_slider, self.sl) for v, v_str in self.levels: # get the size of the label rect = painter.drawText(core.Rect(), QtCore.Qt.TextDontPrint, v_str) if self.sl.orientation() == QtCore.Qt.Horizontal: x_loc = widgets.Style.sliderPositionFromValue( self.sl.minimum(), self.sl.maximum(), v, available) # I assume the offset is half the length of slider, therefore # + length//2 x_loc += length // 2 # left bound of the text = center - half of text width + L_margin left = x_loc - rect.width() // 2 + self.left_margin bottom = self.rect().bottom() # enlarge margins if clipping if v == self.sl.minimum(): if left <= 0: self.left_margin = rect.width() // 2 - x_loc if self.bottom_margin <= rect.height(): self.bottom_margin = rect.height() self.layout.setContentsMargins(self.left_margin, self.top_margin, self.right_margin, self.bottom_margin) if v == self.sl.maximum( ) and rect.width() // 2 >= self.right_margin: self.right_margin = rect.width() // 2 self.layout.setContentsMargins(self.left_margin, self.top_margin, self.right_margin, self.bottom_margin) else: y_loc = widgets.Style.sliderPositionFromValue( self.sl.minimum(), self.sl.maximum(), v, available, upsideDown=True) bottom = y_loc + length // 2 + rect.height( ) // 2 + self.top_margin - 3 # there is a 3 px offset that I can't attribute to any metric left = self.left_margin - rect.width() if left <= 0: self.left_margin = rect.width() + 2 self.layout.setContentsMargins(self.left_margin, self.top_margin, self.right_margin, self.bottom_margin) painter.drawText(left, bottom, v_str) return
def paintEvent(self, event): # Draw time scale = self.get_scale() with gui.Painter(self) as qp: qp.set_color(self.text_color) qp.setFont(self.text_font) qp.use_antialiasing() w = 0 while (w := w + 100) <= self.width(): time_string = helpers.format_seconds(w * scale) rect = core.Rect(w - 50, 0, 100, 100) qp.drawText(rect, constants.ALIGN_H_CENTER, time_string) # Draw down line qp.set_pen(color=PEN_COLOR, width=5) qp.drawLine(0, 40, self.width(), 40) # Draw dash lines point = 0 qp.set_pen(color=self.text_color) qp.drawLine(0, 40, self.width(), 40) while point <= self.width(): y2 = 30 if point % 30 != 0 else 20 qp.drawLine(3 * point, 40, 3 * point, y2) point += 10 if self._position is not None and self._is_in: qp.drawLine(self._position.x(), 0, self._position.x(), 40) poly = gui.Polygon() if self._position is not None: val = self.pointer_time_pos / self.get_scale() line = core.Line(val, 40, val, self.height()) poly.add_points((val - 10, 20), (val + 10, 20), (val, 40)) else: line = core.Line(0, 0, 0, self.height()) poly.add_points((-10, 20), (10, 20), (0, 40)) # Draw samples t = 0.0 for sample in self.video_samples: scaled_dur = sample.duration / scale scaled_t = t / scale t += sample.duration # Clear clip path with qp.clip_path() as path: rect = core.RectF(scaled_t, 50, scaled_dur, 200) path.addRoundedRect(rect, 10, 10) # Draw sample path = gui.PainterPath() qp.set_pen(color=sample.color) rect = core.RectF(scaled_t, 50, scaled_dur, 50) path.addRoundedRect(rect, 10, 10) sample.start_pos = scaled_t sample.end_pos = scaled_t + scaled_dur qp.fillPath(path, sample.color) qp.drawPath(path) # Draw preview pictures if sample.picture is None: continue pic_width = sample.picture.size().width() if pic_width < scaled_dur: width = float(pic_width) pic = sample.picture else: width = scaled_dur pic = sample.picture.copy(0, 0, int(scaled_dur), 45) with qp.clip_path() as path: rect = core.RectF(scaled_t, 52.5, width, 45) path.addRoundedRect(rect, 10, 10) qp.drawPixmap(int(scaled_t), int(52.5), int(width), 45, pic) # Clear clip path with qp.clip_path() as path: path.add_rect(self.rect()) # Draw pointer qp.set_color(PEN_COLOR) qp.set_brush(PEN_COLOR) qp.drawPolygon(poly) qp.drawLine(line)
def get_viewbox(self) -> core.Rect: return core.Rect(self.viewBox())
def get_bounding_rect(self) -> core.Rect: return core.Rect(self.boundingRect())
def heightForWidth(self, width): return self.do_layout(core.Rect(0, 0, width, 0), True)
def setGeometry(self, rect: QtCore.QRect): center = None east_width = 0 west_width = 0 north_height = 0 south_height = 0 super().setGeometry(rect) for wrapper in self.items: item = wrapper.item position = wrapper.position if position == BorderLayout.Position.North: geom = core.Rect(rect.x(), north_height, rect.width(), item.sizeHint().height()) item.setGeometry(geom) north_height += item.geometry().height() + self.spacing() elif position == BorderLayout.Position.South: geom = core.Rect( item.geometry().x(), item.geometry().y(), rect.width(), item.sizeHint().height(), ) item.setGeometry(geom) south_height += item.geometry().height() + self.spacing() geom = core.Rect( rect.x(), rect.y() + rect.height() - south_height + self.spacing(), item.geometry().width(), item.geometry().height(), ) item.setGeometry(geom) elif position == BorderLayout.Position.Center: center = wrapper center_height = rect.height() - north_height - south_height for wrapper in self.items: item = wrapper.item position = wrapper.position if position == BorderLayout.Position.West: geom = core.Rect( rect.x() + west_width, north_height, item.sizeHint().width(), center_height, ) item.setGeometry(geom) west_width += item.geometry().width() + self.spacing() elif position == BorderLayout.Position.East: geom = core.Rect( item.geometry().x(), item.geometry().y(), item.sizeHint().width(), center_height, ) item.setGeometry(geom) east_width += item.geometry().width() + self.spacing() geom = core.Rect( rect.x() + rect.width() - east_width + self.spacing(), north_height, item.geometry().width(), item.geometry().height(), ) item.setGeometry(geom) if center: rect = core.Rect( west_width, north_height, rect.width() - east_width - west_width, center_height, ) center.item.setGeometry(rect)
def get_text_rect(self, text: str) -> core.Rect: return self.drawText(core.Rect(), QtCore.Qt.TextFlag.TextDontPrint, text) # type: ignore
def get_tight_bounding_rect(self, text: str) -> core.Rect: return core.Rect(self.tightBoundingRect(text))
def test_rect(): rect = core.Rect() repr(rect)