Как удалить ненужные отступы / фрейм из фона QGraphicsView в PyQt5 / Qt5? - PullRequest
0 голосов
/ 03 ноября 2019

У меня может быть простая проблема с Qt5. Я новичок в Qt5, так что это недостаток знаний. Я искал более 10 ответов, примеры кода и не нашел решения. Проблема с заполнением, добавленным QGraphicsView (не уверен, но я установил красный фон, чтобы показать его).

Я пробовал много команд, чтобы удалить заполнение, но без результата всегда видны красные линии / цвет фона. Я не могу покрыть весь фон содержимым сцены.

Вот изображение - красный фон `class BoardView (QGraphicsView) 'enter image description here

Здесь частькода, который вызывает проблемы (на мой взгляд) - полный код ниже - вы можете запустить его, так как ссылка только на Qt5. Требуется Python 3.7 или аналогичный.

У меня есть гипотеза, что mayb fitInView () добавляет некоторый запас, но я не уверен, или я не очищаю все поля (недостаток знаний в Qt5) - так что, возможно, мне следует использовать другие методы для достижения чистой доски без заполнения/margin.

class BoardView(QGraphicsView):
    def __init__(self):
        super().__init__()
        logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setViewportMargins(0, 0, 0, 0)
        self.setContentsMargins(0, 0, 0, 0)
        self.setFrameShape(QFrame.NoFrame)
        # self.setFrameStyle(QFrame.NoFrame)

        # transparent background
        # self.setStyleSheet('QGraphicsView {background: transparent;}')
        self.setStyleSheet('QGraphicsView { background: red; }')
        logging.debug(self.styleSheet())

        scene = BoardScene()
        self.setScene(scene)
        # no frame


        logging.debug('Here is problem with frames!')
        logging.debug('rect is %s for %s.', self.rect(), self.__class__.__name__)
        logging.debug('sceneRect is %s for %s.', self.sceneRect(), self.__class__.__name__)

        logging.debug('frameShape is %s for %s.', self.frameShape(), self.__class__.__name__)
        logging.debug('frameStyle is %s for %s.', self.frameStyle(), self.__class__.__name__)

        logging.debug('Why children rect is too small?')
        logging.debug('How to change children rect?')
        logging.debug('childrenRect is %s for %s.', self.childrenRect(), self.__class__.__name__)
        logging.debug('contentsRect is %s for %s.', self.contentsRect(), self.__class__.__name__)
        logging.debug('frameRect is %s for %s.', self.frameRect(), self.__class__.__name__)

        logging.debug('frameSize is %s for %s.', self.frameSize(), self.__class__.__name__)
        logging.debug('frameWidth is %s for %s.', self.frameWidth(), self.__class__.__name__)
        logging.debug('frameGeometry is %s for %s.', self.frameGeometry(), self.__class__.__name__)
        logging.debug('geometry is %s for %s.', self.geometry(), self.__class__.__name__)

    def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
        super().resizeEvent(event)
        self.fitInView(self.scene().board_container, Qt.KeepAspectRatio)

Полностью рабочий код (кроме проблемы с заполнением)

import logging
import sys
import typing

from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QSize, QPoint, Qt, QRect, QMargins
from PyQt5.QtGui import QFont, QPaintEvent, QPainter, QBrush, QColor, QPen
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QSizePolicy, QVBoxLayout, QHBoxLayout, QGraphicsWidget, \
    QGraphicsScene, QGraphicsView, QGraphicsGridLayout, QStyleOptionGraphicsItem, QGraphicsSceneMouseEvent, QFrame


class Application(QApplication):
    pass


class SquareWidget(QGraphicsWidget):
    def __init__(self, color):
        super().__init__()
        if color:
            self.color = QtCore.Qt.white
        else:
            self.color = QtCore.Qt.black

        # set some size
        self.setMinimumSize(2.0, 2.0)

    def paint(self, painter: QtGui.QPainter, option: QStyleOptionGraphicsItem, widget: typing.Optional[QWidget] = ...) -> None:
        painter.fillRect(option.rect, self.color)


class BoardContainer(QGraphicsWidget):
    def __init__(self):
        super().__init__()
        # require initialization
        self.resize(16.0, 16.0)
        logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)
        logging.debug('geometry is %s for %s.', self.geometry(), self.__class__.__name__)

        grid = QGraphicsGridLayout()
        logging.debug('spacing %s %s', (grid.verticalSpacing(), grid.horizontalSpacing()),
                      self.__class__.__name__)
        grid.setSpacing(0)
        logging.debug('spacing %s %s', (grid.verticalSpacing(), grid.horizontalSpacing()),
                      self.__class__.__name__)
        grid.setContentsMargins(0.0, 0.0, 0.0, 0.0)
        self.setLayout(grid)
        for row in range(8):
            for column in range(8):
                square_widget = SquareWidget((row + column) % 2)
                grid.addItem(square_widget, row, column)
        grid.activate()
        logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)


class BoardScene(QGraphicsScene):
    def __init__(self):
        super().__init__()
        self.board_container = board_container = BoardContainer()
        self.addItem(board_container)


class BoardView(QGraphicsView):
    def __init__(self):
        super().__init__()
        logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setViewportMargins(0, 0, 0, 0)
        self.setContentsMargins(0, 0, 0, 0)
        self.setFrameShape(QFrame.NoFrame)
        # self.setFrameStyle(QFrame.NoFrame)

        # transparent background
        # self.setStyleSheet('QGraphicsView {background: transparent;}')
        self.setStyleSheet('QGraphicsView { background: red; }')
        logging.debug(self.styleSheet())

        scene = BoardScene()
        self.setScene(scene)
        # no frame


        logging.debug('Here is problem with frames!')
        logging.debug('rect is %s for %s.', self.rect(), self.__class__.__name__)
        logging.debug('sceneRect is %s for %s.', self.sceneRect(), self.__class__.__name__)

        logging.debug('frameShape is %s for %s.', self.frameShape(), self.__class__.__name__)
        logging.debug('frameStyle is %s for %s.', self.frameStyle(), self.__class__.__name__)

        logging.debug('Why children rect is too small?')
        logging.debug('How to change children rect?')
        logging.debug('childrenRect is %s for %s.', self.childrenRect(), self.__class__.__name__)
        logging.debug('contentsRect is %s for %s.', self.contentsRect(), self.__class__.__name__)
        logging.debug('frameRect is %s for %s.', self.frameRect(), self.__class__.__name__)

        logging.debug('frameSize is %s for %s.', self.frameSize(), self.__class__.__name__)
        logging.debug('frameWidth is %s for %s.', self.frameWidth(), self.__class__.__name__)
        logging.debug('frameGeometry is %s for %s.', self.frameGeometry(), self.__class__.__name__)
        logging.debug('geometry is %s for %s.', self.geometry(), self.__class__.__name__)

    def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
        super().resizeEvent(event)
        self.fitInView(self.scene().board_container, Qt.KeepAspectRatio)


class BoardWidget(QWidget):
    def __init__(self):
        super().__init__()
        logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)

        grid = QGridLayout()

        board_view = BoardView()
        grid.addWidget(board_view, 0, 0)

        self.setLayout(grid)


def main():
    # show exceptions
    def excepthook(cls, exception, traceback):
        sys.__excepthook__(cls, exception, traceback)
    sys.excepthook = excepthook

    logging.basicConfig(level=logging.DEBUG)
    app = Application(sys.argv)
    app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)

    default_font = QFont()
    default_font.setPointSize(12)
    app.setFont(default_font)

    board_widget = BoardWidget()
    board_widget.setMinimumSize(640, 640)
    board_widget.show()

    sys.exit(app.exec())


if __name__ == '__main__':
    main()

1 Ответ

1 голос
/ 03 ноября 2019

Да, действительно fitInView добавляет некоторое «осторожное» поле, чтобы гарантировать отображение всего графического элемента;Я не могу сказать вам, как это вычисляется, но я предполагаю, что оно основано на shape boundingRect, включая (возможное) перо виджета.

В качестве возможного решения,так как я предполагаю, что ширина и высота платы одинаковы, и она всегда будет использовать одну и ту же ручку (в данном случае QtCore.Qt.NoPen, которая обычно "переводит" геометрию на 0,5 пикселя, предполагая косметику ручка размером 1 пиксель ), а верхняя левая позиция доски всегда будет (0, 0), я бы предложил следующее решение.

Она работает почти так же, как fitInView (хотячрезмерно упрощая его (), используя минимальную ширину / высоту, чтобы получить возможный масштаб для преобразования графического представления. Это не всегда будет идеальным по пикселям, но я думаю, что это было бы хорошим подходом для начинающих.

Имейте в виду, что у вас (возможно) всегда будет хотя бы одна пиксельная ошибка: есливы используете целочисленное значение для min, и вы, вероятно, в конечном итоге получите на пиксель меньше необходимого (последняя строка или столбец будет на пиксель короче), а если вы используете значение с плавающей запятой 1.0, это, вероятно, будетпиксель больше. Это зависит от того, как работает QPainter, и, если вы не учитываете это в методе SquareWidget.paint(), в какой-то момент что-то всегда будет отключено.

    def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
        super().resizeEvent(event)
        minSize = min(event.size().width(), event.size().height()) - 1
        # alternatively use "- 1." to get the full square sizes
        scale = minSize / self.sceneRect().width()
        self.setTransform(QtGui.QTransform().scale(scale, scale))
...