簡體   English   中英

使用 PyQt5 繪制正確的網格

[英]Draw a correct grid with PyQt5

我在 PyQt5 上有點掙扎:我必須實現 Conway's Game of Life,我從 GUI 常規設置開始。 我想過堆疊(垂直)兩個小部件,一個用於顯示游戲板,另一個包含按鈕和滑塊。

這就是我想出的(我是個菜鳥)

在此處輸入圖片說明

我想相對於邊緣正確地擬合網格。 看起來它在專用畫布下方構建了網格:首先修復畫布然后在其上繪畫會很棒,但是布局、小部件和所有這些都讓我大吃一驚。

這是我的(寫得很快,寫得不好)代碼

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_()

感謝您的幫助,祝您有美好的一天!

像素圖未以完整尺寸顯示的原因是因為您對窗口像素圖都使用了WINDOW_WIDTHWINDOW_HEIGHT 由於窗口還包含工具欄和它自己的邊距,因此您強制它比它應該的小,因此“剪掉”了板。

更簡單的解決方案是設置標簽的scaledContents屬性:

    self.label.setScaledContents(True)

但結果會有點難看,因為標簽的尺寸會比您繪制的像素圖略小,使其變得模糊。

另一種(更好)的可能性是在窗口顯示設置固定大小,以便 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_()

即使它不是您問題的一部分,我也會向您建議一個略有不同的概念,它不涉及 QLabel。

使用您的方法,您將面臨兩種可能性:

  1. 整個 QPixmap 的連續重繪:您無法輕易從已繪制的表面“清除”某些內容,如果您有移動或消失的對象,您將需要它
  2. 添加必須手動移動的自定義小部件(並且計算它們相對於像素圖的位置將是一個嚴重的 PITA)

更好的解決方案是完全避免使用 QLabel,並使用自定義繪畫實現您自己的小部件。

這是一個簡單的例子:

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))

現在您不再需要make_grid ,您可以使用Grid代替 QLabel。

請注意,我刪除了一個像素來計算正方形大小,否則將不會顯示最后一行/列線,就像您的像素圖中發生的那樣(考慮在 20x20 邊正方形中,將從 0.5 開始的 20px 線將在像素處剪裁19.5)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM