Как нарисовать правильную сетку на PyQt?

Рассмотрим этот маленький фрагмент:

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5.QtWidgets import QMenu
from PyQt5.QtGui import QKeySequence
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QCursor


def create_action(parent, text, slot=None,
                  shortcut=None, shortcuts=None, shortcut_context=None,
                  icon=None, tooltip=None,
                  checkable=False, checked=False):
    action = QtWidgets.QAction(text, parent)

    if icon is not None:
        action.setIcon(QIcon(':/%s.png' % icon))
    if shortcut is not None:
        action.setShortcut(shortcut)
    if shortcuts is not None:
        action.setShortcuts(shortcuts)
    if shortcut_context is not None:
        action.setShortcutContext(shortcut_context)
    if tooltip is not None:
        action.setToolTip(tooltip)
        action.setStatusTip(tooltip)
    if checkable:
        action.setCheckable(True)
    if checked:
        action.setChecked(True)
    if slot is not None:
        action.triggered.connect(slot)

    return action


class Settings():

    WIDTH = 20
    HEIGHT = 15
    NUM_BLOCKS_X = 10
    NUM_BLOCKS_Y = 14


class QS(QtWidgets.QGraphicsScene):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        width = Settings.NUM_BLOCKS_X * Settings.WIDTH
        height = Settings.NUM_BLOCKS_Y * Settings.HEIGHT
        self.setSceneRect(0, 0, width, height)
        self.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)


class QV(QtWidgets.QGraphicsView):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.view_menu = QMenu(self)
        self.create_actions()

    def create_actions(self):
        act = create_action(self.view_menu, "Zoom in",
                            slot=self.on_zoom_in,
                            shortcut=QKeySequence("+"), shortcut_context=Qt.WidgetShortcut)
        self.view_menu.addAction(act)

        act = create_action(self.view_menu, "Zoom out",
                            slot=self.on_zoom_out,
                            shortcut=QKeySequence("-"), shortcut_context=Qt.WidgetShortcut)
        self.view_menu.addAction(act)
        self.addActions(self.view_menu.actions())

    def on_zoom_in(self):
        if not self.scene():
            return

        self.scale(1.5, 1.5)

    def on_zoom_out(self):
        if not self.scene():
            return

        self.scale(1.0 / 1.5, 1.0 / 1.5)

    def drawBackground(self, painter, rect):
        gr = rect.toRect()
        start_x = gr.left() + Settings.WIDTH - (gr.left() % Settings.WIDTH)
        start_y = gr.top() + Settings.HEIGHT - (gr.top() % Settings.HEIGHT)
        painter.save()
        painter.setPen(QtGui.QColor(60, 70, 80).lighter(90))
        painter.setOpacity(0.7)

        for x in range(start_x, gr.right(), Settings.WIDTH):
            painter.drawLine(x, gr.top(), x, gr.bottom())

        for y in range(start_y, gr.bottom(), Settings.HEIGHT):
            painter.drawLine(gr.left(), y, gr.right(), y)

        painter.restore()

        super().drawBackground(painter, rect)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    a = QS()
    b = QV()
    b.setScene(a)
    # b.resize(800,600)
    b.show()
    sys.exit(app.exec_())

Если мы запустим его, мы увидим, что числовой блок сеток в порядке, как указано 8x10:

введите описание изображения здесь

Теперь скажем, что я устанавливаю NUM_BLOCKS_X=3 и NUM_BLOCKS_Y=2, выход будет следующим:

введите описание изображения здесь

Это неправильно! Я определенно не хочу этого, я бы хотел, чтобы QGraphicsView был правильно сжат до заданных параметров сетки.

1-й вопрос: Как я могу это достичь?

Еще одна вещь, которую я хотел бы знать: рассмотрим опубликованный фрагмент, в котором сетка 10x8, а затем измените размер QGraphicsWidget на 800x600, выход будет:

введите описание изображения здесь

Но я хотел бы знать, как я могу рисовать только область QGraphicsScene. Прямо сейчас я использую rect в drawBackground.

Итак, мой второй вопрос: как рисовать сетку только внутри области QGraphicsScene?

Одна из основных проблем появляется, когда я уменьшаю масштаб, в этом случае я хотел бы видеть только размер QGraphicsScene, потому что я буду добавлять элементы только в этом регионе, позвольте назвать его "доступным" регионом. Как вы можете видеть, прямо сейчас он рисует линии сетки на "непритязательной" области и что действительно запутывает

Ответ 1

Я бы нарисовал сетку в Сцене, как это:

class QS(QtWidgets.QGraphicsScene):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        width = Settings.NUM_BLOCKS_X * Settings.WIDTH
        height = Settings.NUM_BLOCKS_Y * Settings.HEIGHT
        self.setSceneRect(0, 0, width, height)
        self.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)

        for x in range(0,Settings.NUM_BLOCKS_X+1):
            xc = x * Settings.WIDTH
            self.addLine(xc,0,xc,height)

        for y in range(0,Settings.NUM_BLOCKS_Y+1):
            yc = y * Settings.HEIGHT
            self.addLine(0,yc,width,yc)

EDIT: Дополнительная видимость/прозрачность:

class QS(QtWidgets.QGraphicsScene):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.lines = []

        self.draw_grid()
        self.set_opacity(0.3)
        #self.set_visible(False)
        #self.delete_grid()

    def draw_grid(self):
        width = Settings.NUM_BLOCKS_X * Settings.WIDTH
        height = Settings.NUM_BLOCKS_Y * Settings.HEIGHT
        self.setSceneRect(0, 0, width, height)
        self.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)

        pen = QPen(QColor(255,0,100), 1, Qt.SolidLine)

        for x in range(0,Settings.NUM_BLOCKS_X+1):
            xc = x * Settings.WIDTH
            self.lines.append(self.addLine(xc,0,xc,height,pen))

        for y in range(0,Settings.NUM_BLOCKS_Y+1):
            yc = y * Settings.HEIGHT
            self.lines.append(self.addLine(0,yc,width,yc,pen))

    def set_visible(self,visible=True):
        for line in self.lines:
            line.setVisible(visible)

    def delete_grid(self):
        for line in self.lines:
            self.removeItem(line)
        del self.lines[:]

    def set_opacity(self,opacity):
        for line in self.lines:
            line.setOpacity(opacity)

Вам нужно добавить импорт: from PyQt5.QtGui import QPen, QColor

EDIT2: Рисуем прямоугольник в сцене:

def draw_insert_at_marker(self):
    w = Settings.WIDTH * 3
    h = Settings.HEIGHT

    r = QRectF(7 * Settings.WIDTH, 7 * Settings.HEIGHT, w, h)
    gradient = QLinearGradient(r.topLeft(), r.bottomRight())
    gradient.setColorAt(1, QColor(255, 255, 255, 0))
    gradient.setColorAt(0, QColor(255, 255, 255, 127))
    rect = self.addRect(r, Qt.white, gradient)

Ответ 2

Вы можете получить сетку автоматического изменения размера из библиотеки:

grid = QtGui.QGridLayout ()

http://zetcode.com/gui/pyqt4/layoutmanagement/

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