【发布时间】:2020-07-20 16:35:50
【问题描述】:
我包含了整个代码 - 问题是(我相信!)第 145-153 行的函数 adjust_gx..
问题是 QGraphicsScene 没有正确缩放到 QGraphicsView。我希望使用一组标准形状并填充一个网格,该网格可以根据窗口或单元格大小值调整大小。 - 但它的渲染非常小,或者以一种悲惨的方式调整大小。
我找到了放大它的方法,但是当“大小”变化(范围在 16 到 120 之间)或窗口边界发生变化时,它会完全不同步。
我发现的大部分建议都是关于使用 scene.itemsBoundingRect()- 我相信我使用正确。
您可以在屏幕截图中看到绘制的图形有多小。
from itertools import product
from math import ceil
from random import choices
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QPen, QPainterPath
from PyQt5.QtWidgets import QGraphicsScene
from PyQt5.QtCore import Qt, QRect, QPointF
class Art:
normal = 100
w_off = 2
line = normal / 2
t_l = QPointF(-line + w_off, -line + w_off)
t_r = QPointF(+line - w_off, -line + w_off)
b_r = QPointF(+line - w_off, +line - w_off)
b_l = QPointF(-line + w_off, +line - w_off)
@staticmethod
def _poly(edges):
pt = [Art.t_r, Art.b_r, Art.b_l, Art.t_l]
path = QPainterPath()
path.moveTo(Art.t_l)
for i in range(4):
path.lineTo(pt[i]) if edges[i] else path.moveTo(pt[i])
return path
def __init__(self):
self.x = 1
self.y = 1
self.z = 1
self.cells = {}
self.scenes = []
self.polygons = {x: self._poly(x) for x in product((False, True), repeat=4)}
def reset(self, x, y, z):
self.x = x
self.y = y
self.z = z
self.cells = {tuple((ix, iy, iz)): tuple(choices([True, False], k=4))
for ix in range(self.x)
for iy in range(self.y)
for iz in range(self.z)
}
for scene in self.scenes:
scene.clear()
def draw(self):
pen = QPen(Qt.black, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
for x, y, z in self.cells:
cx1 = x * Art.normal
cy1 = y * Art.normal
shape = self.polygons[self.cells[x, y, z]]
self.scenes[z].addPath(shape.translated(QPointF(cx1, cy1)), pen)
class MyDialog(QtWidgets.QDialog):
"""
Is the main application window, which is made of two parts:
One one side is the pane of tabs which display the maze
On the other are the configurable values of the maze (levels)
"""
def __init__(self):
self.pane = None
self.form = None
self.horizontal_layout = None
super().__init__()
def resizeEvent(self, event):
self.pane.resize()
super().resizeEvent(event)
def setup(self):
self.pane = Pane(self)
self.form = Form(self)
self.setWindowTitle("Configure")
self.setObjectName("Dialog")
size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
size_policy.setHorizontalStretch(0)
size_policy.setVerticalStretch(0)
size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
self.setSizePolicy(size_policy)
self.horizontal_layout = QtWidgets.QHBoxLayout(self)
self.horizontal_layout.setObjectName("horizontal_layout")
self.pane.setup(self.horizontal_layout)
self.form.setup(self.horizontal_layout)
self.pane.connect_form(self.form)
class Pane:
def __init__(self, parent):
self.parent = parent # This is the widget.
self.layout = None # This is the parent's layout.
self.form = None # This is the form used to control things.
self.tab_widget = None # This is the tab widget, which can be fully replaced.
# Everything below are default values.
self.offset = 12
self.demi = self.offset // 2
self.tmp_idx = 0
self.levels = 4
self.width = 3
self.height = 3
self.cell_size = 100
self.art = None
self.level_tabs = []
self.level_gx = []
def setup(self, parent_layout):
self.layout = parent_layout
self.art = Art()
tab_widget_dim = QRect(0, 0, 360, 380) # This is the size of the initial tab_widget
self.set_tab_widget(tab_widget_dim)
def connect_form(self, form):
self.form = form
self.form.set_values({'Size': self.cell_size, 'Levels': self.levels})
self.form.set_listen({'Size': self.resize, 'Levels': self.re_depth})
def make_tab_widget(self):
self.tab_widget = QtWidgets.QTabWidget(self.parent)
self.tab_widget.setMinimumSize(QtCore.QSize(360, 380))
self.tab_widget.setTabPosition(QtWidgets.QTabWidget.North)
self.tab_widget.setObjectName("tab_widget")
def reset_tab_widget(self):
self.tmp_idx = 0
prev_widget = self.tab_widget
self.make_tab_widget()
if prev_widget:
self.tmp_idx = prev_widget.currentIndex()
self.parent.horizontal_layout.replaceWidget(prev_widget, self.tab_widget)
prev_widget.clear()
prev_widget.close()
else:
self.parent.horizontal_layout.addWidget(self.tab_widget)
if self.art:
for lt in self.level_tabs:
lt.close()
self.level_tabs = []
for gx in self.level_gx:
gx.close()
self.level_gx = []
self.art.scenes = []
def adjust_gx(self, gv, scene, frame):
# Resize the graphics port to fit the outer frame. (GUI pixels)
gv.resize(frame.width(), frame.height())
# Seems to make no difference.
boundary = scene.itemsBoundingRect()
gv.fitInView(boundary, Qt.KeepAspectRatio)
scene.setSceneRect(boundary)
# Also tried the below...
# gv.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
def set_tab_widget(self, tab_widget_dim):
self.reset_tab_widget() # initialises / replaces a QTabWidget into the horizontal_layout
for level in range(self.levels):
tab = QtWidgets.QWidget()
tab.setObjectName(f"T{level}")
self.tab_widget.addTab(tab, f"{level + 1}")
self.level_tabs.append(tab)
scene = QGraphicsScene()
self.art.scenes.append(scene)
tab.resize(tab_widget_dim.width(), tab_widget_dim.height())
gv = QtWidgets.QGraphicsView(scene, tab)
gv.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
gv.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
gv.setMinimumSize(QtCore.QSize(340, 340))
gv.setObjectName(f"Gx{level}")
self.level_gx.append(gv)
# self.adjust_gx(gv, scene, tab_widget_dim)
self.art.reset(self.width, self.height, self.levels)
self.art.draw()
for idx in range(self.levels):
self.adjust_gx(self.level_gx[idx], self.art.scenes[idx], tab_widget_dim)
self.tab_widget.setCurrentIndex(self.tmp_idx % self.levels)
def resize(self):
if self.tab_widget:
self.tab_widget.hide()
idx = self.tab_widget.currentIndex()
dt = QRect(self.level_tabs[idx].frameGeometry())
self.cell_size = self.form.get_value('Size')
self.width = ceil((dt.width() - self.offset) // self.cell_size)
self.height = ceil((dt.height() - self.offset) // self.cell_size)
self.form.set_texts({'Width': f"{self.width}", 'Height': f"{self.height}"})
self.set_tab_widget(dt)
self.tab_widget.show()
self.parent.update()
def re_depth(self):
self.levels = self.form.get_value('Levels')
self.resize()
class Form(QtWidgets.QWidget):
def __init__(self, parent):
self.parent = parent
self.form = None
self.label_text = ('Levels', 'Size', 'Width', 'Height')
self.minimums = (1, 16)
self.maximums = (20, 320)
self.labels = []
self.fields = {}
super().__init__(self.parent)
def setup(self, parent_layout):
self.form = QtWidgets.QFormLayout(self)
self.form.setObjectName("form")
self.setMaximumSize(QtCore.QSize(160, 200))
self.setMinimumSize(QtCore.QSize(160, 200))
# Create the fields within the form layout
for field in range(4):
if field < 2:
ui = QtWidgets.QSpinBox(self)
ui.setMinimum(self.minimums[field])
ui.setMaximum(self.maximums[field])
ui.setMinimumSize(QtCore.QSize(60, 0))
ui.setObjectName(f"f_{field}")
else:
ui = QtWidgets.QLabel(self)
ui.setObjectName(f"f_{field}")
ui.setText(f"{0}")
self.form.setWidget(field + 1, QtWidgets.QFormLayout.FieldRole, ui)
self.fields[self.label_text[field]] = ui
label = QtWidgets.QLabel(self)
label.setObjectName(f"label_{field}")
label.setText(self.label_text[field])
self.form.setWidget(field + 1, QtWidgets.QFormLayout.LabelRole, label)
self.labels.append(label)
parent_layout.addWidget(self)
def get_value(self, name):
return self.fields[name].value()
def set_values(self, config):
for k in config.keys():
self.fields[k].setValue(config[k])
def set_texts(self, config):
for k in config.keys():
self.fields[k].setText(config[k])
def set_listen(self, config):
for k in config.keys():
self.fields[k].valueChanged.connect(config[k])
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
dialog = MyDialog()
dialog.setup()
dialog.show()
result = app.exec_()
【问题讨论】:
标签: python python-3.x macos pyqt pyqt5