예제 #1
0
    def paint(
        self,
        painter: QtGui.QPainter,
        option: QtWidgets.QStyleOptionGraphicsItem,
        widget: QtWidgets.QWidget = None,
    ):
        view = self.scene().views()[0]
        view_width = view.mapToScene(0, 0, self.width,
                                     1).boundingRect().width()
        width, unit = self.getWidthAndUnit(view_width)
        # Current scale
        text = f"{width * self.units[self.unit] / self.units[unit]:.3g} {unit}"
        width = width * view.transform().m11()

        fm = QtGui.QFontMetrics(self.font, painter.device())
        path = QtGui.QPainterPath()
        path.addText(
            self.width / 2.0 - fm.boundingRect(text).width() / 2.0,
            fm.ascent(),
            self.font,
            text,
        )

        painter.strokePath(path, QtGui.QPen(QtCore.Qt.black, 2.0))
        painter.fillPath(path, QtGui.QBrush(self.color,
                                            QtCore.Qt.SolidPattern))

        # Draw the bar
        rect = QtCore.QRectF(self.width / 2.0 - width / 2.0, fm.height(),
                             width, self.height)
        painter.setPen(QtGui.QPen(QtCore.Qt.black, 1.0))
        painter.setBrush(QtGui.QBrush(self.color, QtCore.Qt.SolidPattern))
        painter.drawRect(rect)
예제 #2
0
 def paintEvent(self, event):
     super().paintEvent(event)
     painter = QPainter(self)
     brush = QBrush()
     brush.setColor(self.color)
     brush.setStyle(Qt.SolidPattern)
     rect = QRect(3, 3, -6 + self.width(), -6 + painter.device().height())
     painter.setBrush(brush)
     painter.fillRect(rect, brush)
예제 #3
0
    def assert_equal(self):
        __tracebackhide__ = True
        self.end()
        self.different_pixels = 0
        actual_image: QImage = self.actual.device().toImage()
        expected_image: QImage = self.expected.device().toImage()
        diff_pixmap = QPixmap(actual_image.width(), actual_image.height())
        diff = QPainter(diff_pixmap)
        try:
            white = QColor('white')
            diff.fillRect(0, 0, actual_image.width(), actual_image.height(),
                          white)
            for x in range(actual_image.width()):
                for y in range(actual_image.height()):
                    actual_colour = actual_image.pixelColor(x, y)
                    expected_colour = expected_image.pixelColor(x, y)
                    diff.setPen(
                        self.diff_colour(actual_colour, expected_colour, x, y))
                    diff.drawPoint(x, y)
        finally:
            diff.end()
        diff_image: QImage = diff.device().toImage()

        display_diff(actual_image, diff_image, expected_image,
                     self.different_pixels)

        if self.different_pixels == 0:
            return
        actual_image.save(str(self.work_dir / (self.name + '_actual.png')))
        expected_image.save(str(self.work_dir / (self.name + '_expected.png')))
        diff_path = self.work_dir / (self.name + '_diff.png')
        is_saved = diff_image.save(str(diff_path))
        diff_width = self.diff_max_x - self.diff_min_x + 1
        diff_height = self.diff_max_y - self.diff_min_y + 1
        diff_section = QImage(diff_width, diff_height, QImage.Format_RGB32)
        diff_section_painter = QPainter(diff_section)
        try:
            diff_section_painter.drawPixmap(0, 0, diff_width, diff_height,
                                            QPixmap.fromImage(diff_image),
                                            self.diff_min_x, self.diff_min_y,
                                            diff_width, diff_height)
        finally:
            diff_section_painter.end()
        # To see an image dumped in the Travis CI log, copy the text from the
        # log, and paste it in test_pixmap_differ.test_decode_image.
        print(f'Encoded image of differing section '
              f'({self.diff_min_x}, {self.diff_min_y}) - '
              f'({self.diff_max_x}, {self.diff_max_y}):')
        print(encode_image(diff_section))
        message = f'Found {self.different_pixels} different pixels, '
        message += f'see' if is_saved else 'could not write'
        message += f' {diff_path.relative_to(Path(__file__).parent.parent)}.'
        assert self.different_pixels == 0, message
예제 #4
0
    def paint(
        self,
        painter: QtGui.QPainter,
        option: QtWidgets.QStyleOptionGraphicsItem,
        widget: QtWidgets.QWidget = None,
    ):

        fm = QtGui.QFontMetrics(self.font, painter.device())
        path = QtGui.QPainterPath()
        path.addText(0, fm.ascent(), self.font, self.text())

        painter.strokePath(path, QtGui.QPen(QtCore.Qt.black, 2.0))
        painter.fillPath(path, QtGui.QBrush(self.color,
                                            QtCore.Qt.SolidPattern))
예제 #5
0
    def paint(
        self,
        painter: QtGui.QPainter,
        option: QtWidgets.QStyleOptionGraphicsItem,
        widget: QtWidgets.QWidget = None,
    ):
        width = painter.viewport().width()

        fm = QtGui.QFontMetrics(self.font, painter.device())

        rect = QtCore.QRect(0, fm.height(), width, self.height)
        painter.drawPixmap(rect, self.pixmap)
        painter.setPen(QtGui.QPen(QtCore.Qt.black, 2.0))
        painter.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.NoBrush))
        painter.drawRect(rect)

        path = QtGui.QPainterPath()
        # Todo: find a better pad value
        path.addText(
            width - fm.boundingRect(self.unit).width() - 10,
            fm.ascent(),
            self.font,
            self.unit,
        )

        vrange = self.vmax - self.vmin
        if vrange <= 0.0:
            return

        for value in self.niceTextValues(7, trim=1):
            x = width * (value - self.vmin) / vrange
            text = f"{value:.6g}"
            path.addText(
                x - fm.boundingRect(text).width() / 2.0,
                fm.ascent(),
                self.font,
                text,
            )
            if self.checkmarks:
                path.addRect(
                    x - fm.lineWidth() / 2.0,
                    fm.ascent() + fm.underlinePos(),
                    fm.lineWidth() * 2.0,
                    fm.underlinePos(),
                )
        painter.strokePath(path, QtGui.QPen(QtCore.Qt.black, 2.0))
        painter.fillPath(path, QtGui.QBrush(self.color,
                                            QtCore.Qt.SolidPattern))
    def draw_monitor_label(self, painter: QPainter, rect: QRectF, txt: str):
        painter.save()
        font = self.monitor_label_font
        font_metrics = QFontMetrics(font, painter.device())

        bounding_rect = font_metrics.boundingRect(rect.toRect(), 0, txt)
        x_factor = rect.width() / bounding_rect.width()
        y_factor = rect.height() / bounding_rect.height()
        factor = min(x_factor, y_factor)

        font.setPointSizeF(font.pointSizeF() * factor)
        painter.setFont(font)
        painter.setPen(self.monitor_label_font_color)

        painter.drawText(rect, Qt.AlignCenter, txt)
        painter.restore()
예제 #7
0
    def paint(
        self,
        painter: QtGui.QPainter,
        option: QtWidgets.QStyleOptionGraphicsItem,
        widget: QtWidgets.QWidget = None,
    ):

        fm = QtGui.QFontMetrics(self.font, painter.device())
        y = fm.ascent()
        for text, color in zip(self._texts, self.colors):
            path = QtGui.QPainterPath()
            path.addText(0, y, self.font, text)

            painter.strokePath(path, QtGui.QPen(QtCore.Qt.black, 2.0))
            painter.fillPath(path, QtGui.QBrush(color, QtCore.Qt.SolidPattern))

            y += fm.height()
예제 #8
0
def render_display(display: GameDisplay,
                   painter: QPainter,
                   is_closed: bool = True):
    """ Check scene size, render, then clear scene.

    You have to clear the scene to avoid a crash after running several unit
    tests.
    :param display: display widget whose children contain a QGraphicsView to
        render.
    :param painter: a canvas to render on
    :param is_closed: True if the display should be closed after rendering. Be
        sure to close the display before exiting the test, if it contains any
        items with reference cycles back to the scene.
    """
    __tracebackhide__ = True
    try:
        for child in display.children():
            if isinstance(child, QGraphicsView):
                view = child
                break
        else:
            raise ValueError("No QGraphicsView in display's children.")

        view.grab()  # Force layout to recalculate, if needed.
        scene_size = view.contentsRect().size()
        painter_size = painter.device().size()
        if scene_size != painter_size:
            display_size = find_display_size(display, view, painter_size)
            message = (f"Try resizing display to "
                       f"{display_size.width()}x{display_size.height()}.")
            painter.drawText(
                QRect(0, 0, painter_size.width(), painter_size.height()),
                Qt.AlignCenter | Qt.TextWordWrap, message)
            return
        assert scene_size == painter_size
        view.scene().render(painter)
    finally:
        if is_closed:
            display.close()
예제 #9
0
    def paintEvent(self, _):
        """
        Paints the widget contents
        """
        painter = QPainter(self)

        device = painter.device()

        optimal_vertical_tile_size = device.height() // self.model.height
        optimal_horizontal_tile_size = device.width() // self.model.width

        tile_size = min(optimal_vertical_tile_size,
                        optimal_horizontal_tile_size)

        brush = QBrush()
        brush.setStyle(Qt.SolidPattern)

        for pos in self.model.all_positions():
            block = self.model.state[pos]

            if block is Block.EMPTY:
                continue
            if not block.visible:
                continue

            r, g, b = block.color
            q_color = QColor(r, g, b)
            brush.setColor(q_color)

            x, y = pos
            canvas_x = self.START_X + tile_size * x
            canvas_y = self.START_Y + tile_size * y

            rect = QRect(canvas_x, canvas_y, tile_size, tile_size)
            painter.fillRect(rect, brush)

        painter.end()
예제 #10
0
class PixmapDiffer:
    def __init__(self):
        self.name = None
        self.actual_pixmap = self.expected_pixmap = None
        self.actual = self.expected = None
        self.different_pixels = 0
        self.diff_min_x = self.diff_min_y = None
        self.diff_max_x = self.diff_max_y = None
        self.max_diff = 0

        self.names = set()

        self.work_dir: Path = (Path(__file__).parent.parent / 'tests' /
                               'pixmap_diffs')
        self.work_dir.mkdir(exist_ok=True)
        for work_file in self.work_dir.iterdir():
            if work_file.name == 'README.md':
                continue
            assert work_file.suffix == '.png'
            work_file.unlink()

    @contextmanager
    def create_painters(self,
                        width: int,
                        height: int,
                        name: str,
                        max_diff: int = 0
                        ) -> typing.Iterator[typing.Tuple[QPainter, QPainter]]:
        self.max_diff = max_diff
        try:
            yield self.start(width, height, name)
        finally:
            self.end()
        self.assert_equal()

    def start(self, width: int, height: int,
              name: str) -> typing.Tuple[QPainter, QPainter]:
        """ Create painters for the actual and expected images.

        Caller must either call end() or assert_equal() to properly clean up
        the painters and pixmaps. Caller may either paint through the returned
        painters, or call the end() method and create a new painter on the
        same device. Order matters, though!
        """
        assert name not in self.names, f'Duplicate name: {name!r}.'
        self.names.add(name)
        self.name = name

        white = QColor('white')
        self.actual_pixmap = QPixmap(width, height)
        self.actual_pixmap.fill(white)
        self.actual = QPainter(self.actual_pixmap)
        self.expected_pixmap = QPixmap(width, height)
        self.expected_pixmap.fill(white)
        self.expected = QPainter(self.expected_pixmap)

        return self.actual, self.expected

    def end(self):
        if self.actual and self.actual.isActive():
            self.actual.end()
        if self.expected and self.expected.isActive():
            self.expected.end()

    def assert_equal(self):
        __tracebackhide__ = True
        self.end()
        self.different_pixels = 0
        actual_image: QImage = self.actual.device().toImage()
        expected_image: QImage = self.expected.device().toImage()
        diff_pixmap = QPixmap(actual_image.width(), actual_image.height())
        diff = QPainter(diff_pixmap)
        try:
            white = QColor('white')
            diff.fillRect(0, 0, actual_image.width(), actual_image.height(),
                          white)
            for x in range(actual_image.width()):
                for y in range(actual_image.height()):
                    actual_colour = actual_image.pixelColor(x, y)
                    expected_colour = expected_image.pixelColor(x, y)
                    diff.setPen(
                        self.diff_colour(actual_colour, expected_colour, x, y))
                    diff.drawPoint(x, y)
        finally:
            diff.end()
        diff_image: QImage = diff.device().toImage()

        display_diff(actual_image, diff_image, expected_image,
                     self.different_pixels)

        if self.different_pixels == 0:
            return
        actual_image.save(str(self.work_dir / (self.name + '_actual.png')))
        expected_image.save(str(self.work_dir / (self.name + '_expected.png')))
        diff_path = self.work_dir / (self.name + '_diff.png')
        is_saved = diff_image.save(str(diff_path))
        diff_width = self.diff_max_x - self.diff_min_x + 1
        diff_height = self.diff_max_y - self.diff_min_y + 1
        diff_section = QImage(diff_width, diff_height, QImage.Format_RGB32)
        diff_section_painter = QPainter(diff_section)
        try:
            diff_section_painter.drawPixmap(0, 0, diff_width, diff_height,
                                            QPixmap.fromImage(diff_image),
                                            self.diff_min_x, self.diff_min_y,
                                            diff_width, diff_height)
        finally:
            diff_section_painter.end()
        # To see an image dumped in the Travis CI log, copy the text from the
        # log, and paste it in test_pixmap_differ.test_decode_image.
        print(f'Encoded image of differing section '
              f'({self.diff_min_x}, {self.diff_min_y}) - '
              f'({self.diff_max_x}, {self.diff_max_y}):')
        print(encode_image(diff_section))
        message = f'Found {self.different_pixels} different pixels, '
        message += f'see' if is_saved else 'could not write'
        message += f' {diff_path.relative_to(Path(__file__).parent.parent)}.'
        assert self.different_pixels == 0, message

    def diff_colour(self, actual_colour: QColor, expected_colour: QColor,
                    x: int, y: int):
        diff_size = (abs(actual_colour.red() - expected_colour.red()) +
                     abs(actual_colour.green() - expected_colour.green()) +
                     abs(actual_colour.blue() - expected_colour.blue()))
        if diff_size <= self.max_diff:
            diff_colour = actual_colour.toRgb()
            diff_colour.setAlpha(diff_colour.alpha() // 3)
            return diff_colour
        if self.different_pixels == 0:
            self.diff_min_x = self.diff_max_x = x
            self.diff_min_y = self.diff_max_y = y
        else:
            self.diff_min_x = min(self.diff_min_x, x)
            self.diff_max_x = max(self.diff_max_x, x)
            self.diff_min_y = min(self.diff_min_y, y)
            self.diff_max_y = max(self.diff_max_y, y)

        self.different_pixels += 1
        # Colour
        dr = 0xff
        dg = (actual_colour.green() + expected_colour.green()) // 5
        db = (actual_colour.blue() + expected_colour.blue()) // 5

        # Opacity
        da = 0xff
        return QColor(dr, dg, db, da)