[英]Draw a correct grid with PyQt5
I'm struggling a bit with PyQt5: I have to implement Conway's Game of Life and I started out with the GUI general setup.我在 PyQt5 上有点挣扎:我必须实现 Conway's Game of Life,我从 GUI 常规设置开始。 I thought about stacking (vertically) two widgets, one aimed at displaying the game board and another one containing the buttons and sliders.我想过堆叠(垂直)两个小部件,一个用于显示游戏板,另一个包含按钮和滑块。
This is what I came up with (I'm a total noob)这就是我想出的(我是个菜鸟)
I'd like to fit the grid correctly with respect to the edges.我想相对于边缘正确地拟合网格。 It looks like it builds the grid underneath the dedicated canvas: it would be great to fix the canvas first and then paint on it but this whole thing of layouts, widgets and all that blows my mind.看起来它在专用画布下方构建了网格:首先修复画布然后在其上绘画会很棒,但是布局、小部件和所有这些都让我大吃一惊。
This is my (fastly and poorly written) code这是我的(写得很快,写得不好)代码
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QLabel, QSlider, QPushButton, QWidget
from PyQt5.QtCore import Qt, QRect
from PyQt5.QtGui import QPixmap, QColor, QPainter
WINDOW_WIDTH, WINDOW_HEIGHT = 800, 600
SQUARE_SIDE = 20
ROWS, COLS = int(WINDOW_HEIGHT/SQUARE_SIDE), int(WINDOW_WIDTH/2*SQUARE_SIDE)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
buttons_layout = QHBoxLayout()
self.label = QLabel()
self.label.setContentsMargins(0,0,0,0)
self.label.setStyleSheet('background-color: white; ')
self.label.setAlignment(Qt.AlignCenter)
slider = QSlider(Qt.Horizontal)
start_button = QPushButton('Start')
pause_button = QPushButton('Pause')
reset_button = QPushButton('Reset')
load_button = QPushButton('Load')
save_button = QPushButton('Save')
layout.addWidget(self.label)
buttons_layout.addWidget(start_button)
buttons_layout.addWidget(pause_button)
buttons_layout.addWidget(reset_button)
buttons_layout.addWidget(load_button)
buttons_layout.addWidget(save_button)
buttons_layout.addWidget(slider)
layout.addLayout(buttons_layout)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.make_grid()
def make_grid(self):
_canvas = QPixmap(WINDOW_WIDTH, WINDOW_HEIGHT)
_canvas.fill(QColor("#ffffff"))
self.label.setPixmap(_canvas)
painter = QPainter(self.label.pixmap())
for c in range(COLS):
painter.drawLine(SQUARE_SIDE*c, WINDOW_HEIGHT, SQUARE_SIDE*c, 0)
for r in range(ROWS):
painter.drawLine(0, SQUARE_SIDE*r, WINDOW_WIDTH, SQUARE_SIDE*r)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.setFixedSize(WINDOW_WIDTH, WINDOW_HEIGHT)
window.setWindowTitle("Conway's Game of Life")
window.show()
app.exec_()
Thank you for your help, have a nice day!感谢您的帮助,祝您有美好的一天!
The reason for the pixmap not being show at its full size is because you're using WINDOW_WIDTH
and WINDOW_HEIGHT
for both the window and the pixmap.像素图未以完整尺寸显示的原因是因为您对窗口和像素图都使用了WINDOW_WIDTH
和WINDOW_HEIGHT
。 Since the window also contains the toolbar and its own margins, you're forcing it to be smaller than it should, hence the "clipping out" of the board.由于窗口还包含工具栏和它自己的边距,因此您强制它比它应该的小,因此“剪掉”了板。
The simpler solution would be to set the scaledContents
property of the label:更简单的解决方案是设置标签的scaledContents
属性:
self.label.setScaledContents(True)
But the result would be a bit ugly, as the label will have a size slightly smaller than the pixmap you drawn upon, making it blurry.但结果会有点难看,因为标签的尺寸会比您绘制的像素图略小,使其变得模糊。
Another (and better) possibility would be to set the fixed size after the window has been shown, so that Qt will take care of the required size of all objects:另一种(更好)的可能性是在窗口显示后设置固定大小,以便 Qt 处理所有对象所需的大小:
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
# window.setFixedSize(WINDOW_WIDTH, WINDOW_HEIGHT)
window.setWindowTitle("Conway's Game of Life")
window.show()
window.setFixedSize(window.size())
app.exec_()
Even if it's not part of your question, I'm going to suggest you a slightly different concept, that doesn't involve a QLabel.即使它不是您问题的一部分,我也会向您建议一个略有不同的概念,它不涉及 QLabel。
With your approach, you'll face two possibilities:使用您的方法,您将面临两种可能性:
A better solution would be to avoid at all the QLabel, and implement your own widget with custom painting.更好的解决方案是完全避免使用 QLabel,并使用自定义绘画实现您自己的小部件。
Here's a simple example:这是一个简单的例子:
class Grid(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setMinimumSize(800, 600)
self.columns = 40
self.rows = 30
# some random objects
self.objects = [
(10, 20),
(11, 21),
(12, 20),
(12, 22),
]
def resizeEvent(self, event):
# compute the square size based on the aspect ratio, assuming that the
# column and row numbers are fixed
reference = self.width() * self.rows / self.columns
if reference > self.height():
# the window is larger than the aspect ratio
# use the height as a reference (minus 1 pixel)
self.squareSize = (self.height() - 1) / self.rows
else:
# the opposite
self.squareSize = (self.width() - 1) / self.columns
def paintEvent(self, event):
qp = QPainter(self)
# translate the painter by half a pixel to ensure correct line painting
qp.translate(.5, .5)
qp.setRenderHints(qp.Antialiasing)
width = self.squareSize * self.columns
height = self.squareSize * self.rows
# center the grid
left = (self.width() - width) / 2
top = (self.height() - height) / 2
y = top
# we need to add 1 to draw the topmost right/bottom lines too
for row in range(self.rows + 1):
qp.drawLine(left, y, left + width, y)
y += self.squareSize
x = left
for column in range(self.columns + 1):
qp.drawLine(x, top, x, top + height)
x += self.squareSize
# create a smaller rectangle
objectSize = self.squareSize * .8
margin = self.squareSize* .1
objectRect = QRectF(margin, margin, objectSize, objectSize)
qp.setBrush(Qt.blue)
for col, row in self.objects:
qp.drawEllipse(objectRect.translated(
left + col * self.squareSize, top + row * self.squareSize))
Now you don't need make_grid
anymore, and you can use Grid
instead of the QLabel.现在您不再需要make_grid
,您可以使用Grid
代替 QLabel。
Note that I removed one pixel to compute the square size, otherwise the last row/column lines won't be shown, as happened in your pixmap (consider that in a 20x20 sided square, a 20px line starting from 0.5 would be clipped at pixel 19.5).请注意,我删除了一个像素来计算正方形大小,否则将不会显示最后一行/列线,就像您的像素图中发生的那样(考虑在 20x20 边正方形中,将从 0.5 开始的 20px 线将在像素处剪裁19.5)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.