【问题标题】:Drawing a shape over a widget在小部件上绘制形状
【发布时间】:2021-03-02 13:13:51
【问题描述】:

我需要能够在另一个小部件上绘制一个圆/线,但每次我尝试时,它都会落后。我已经阅读了很多关于在小部件上使用 QPainter 的帖子,但我仍然无法让它工作。

以下是我的应用程序的一个最小示例,我只是想弄清楚在哪里放置一个paintevent函数以使其正常工作。

我的最终目标是让用户画出这样的热数独 - thermo

但我认为,如果我能弄清楚如何在我的 SudokuGrid 上绘制任何东西,我就可以解决剩下的问题

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import *


class SudokuCell(QLineEdit):
    def __init__(self, cell_size, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.cell_size = cell_size
        font = self.font()
        font.setPointSize(24)
        self.setFont(font)
        self.setAlignment(Qt.AlignCenter)
        self.setFixedSize(cell_size, cell_size)
        self.setAutoFillBackground(True)

class SudokuRegion(QWidget):
    def __init__(self, cell_size, narrow_line_width, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.cell_size = cell_size
        self.narrow_line_width = narrow_line_width

        layout = QGridLayout()
        layout.setSpacing(narrow_line_width)
        layout.setContentsMargins(0,0,0,0)
        self.setLayout(layout)

        for i in range(3):
            for j in range(3):
                new_cell = SudokuCell(cell_size, objectName=f"C{i}{j}")
                layout.addWidget(new_cell, i, j)
                               
class SudokuGrid(QWidget):
    def __init__(self, cell_size, wide_line_width, narrow_line_width, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.cell_size = cell_size
        self.wide_line_width = wide_line_width
        self.narrow_line_width = narrow_line_width

        layout = QGridLayout()
        layout.setContentsMargins(wide_line_width,wide_line_width, 0, 0)
        layout.setSpacing(wide_line_width)
        self.setLayout(layout)

        for i in range(3):
            for j in range(3):
                new_region = SudokuRegion(cell_size, narrow_line_width, objectName=f"Region{i * 3 + j}")
                layout.addWidget(new_region, i, j)

class MainWindow(QMainWindow):
    def __init__(self, window_w, window_h, orthogonal_intersection_size, cell_size, wide_line_width, narrow_line_width):
        super().__init__()
        self.window_w = window_w
        self.window_h = window_h
        self.orthogonal_intersection_size = orthogonal_intersection_size
        self.cell_size = cell_size
        self.wide_line_width = wide_line_width
        self.narrow_line_width = narrow_line_width

        # a sudoku grid is exactly this large. Google a sudoku grid if you dont understand
        self.frame_size = 9 * cell_size + 4 * wide_line_width + 6 * narrow_line_width
        self.initUi()

    def initUi(self):
        self.setWindowTitle("Sudoku Solver")
        self.setGeometry(100, 100, self.window_w, self.window_h)

        widget = QWidget(self)
        self.setCentralWidget(widget)

        hor_box = QHBoxLayout()
        widget.setLayout(hor_box)

        self.frame = QFrame(widget)
        self.frame.setFixedSize(self.frame_size, self.frame_size)
        self.frame.setStyleSheet(".QFrame {background-color: black}")

        self.grid = SudokuGrid(self.cell_size, self.wide_line_width, self.narrow_line_width, self.frame)
        hor_box.addWidget(self.frame)

        # other widgets are added to the hor_box later but arent important for this question
        self.show()

def main():
    app = QApplication(sys.argv)
    window = MainWindow(
        window_w=1000,
        window_h=750,
        orthogonal_intersection_size=25,
        cell_size=80,
        wide_line_width=8,
        narrow_line_width=2
    )
    window.show()
    app.exec_()

if __name__ == "__main__":
    main()

【问题讨论】:

    标签: python pyqt pyqt5


    【解决方案1】:

    您可以在 SudokuCell 或 SudokuGrid 中重新实现 paintEvent,但在任何一种情况下,SudokuCell 都需要具有透明背景,以便在除 QLineEdit 文本编辑器之外的所有内容之上绘制热图。我选择了 SudokuCell。

    import sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    
    class SudokuCell(QLineEdit):
        def __init__(self, cell_size, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.cell_size = cell_size
            font = self.font()
            font.setPointSize(24)
            self.setFont(font)
            self.setAlignment(Qt.AlignCenter)
            self.setFixedSize(cell_size, cell_size)
            self.setAutoFillBackground(True)
    
            self.setStyleSheet('''
            SudokuCell {
                background-color: rgba(0, 0, 0, 0);
                border: none;
            }''')
            self.line = QPainterPath()
            self.ellipse = False
            
        def paintEvent(self, event):
            qp = QPainter(self)
            qp.setPen(Qt.NoPen)
            qp.setBrush(Qt.white)
            qp.drawRect(self.rect())
            s = self.cell_size
            if self.ellipse:
                qp.setRenderHint(QPainter.Antialiasing)
                qp.setBrush(Qt.gray)
                qp.drawEllipse(self.rect().center(), s / 2.5, s / 2.5)
            qp.setPen(QPen(Qt.gray, s / 2, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin))
            qp.drawPath(self.line)
            super().paintEvent(event)
    

    注意super().paintEvent(event) 在自定义绘画之后被称为,因此文本编辑器将在任何绘画之上可见。如果背景为白色,它将覆盖自定义绘画。 SudokuGrid 类具有向由行和列指定的任何单元格添加直线或椭圆的方法。

    class SudokuGrid(QWidget):
        ...
    
        def cell(self, row, col):
            region = self.layout().itemAtPosition(row // 3, col // 3).widget()
            return region.layout().itemAtPosition(row % 3, col % 3).widget()
    
        def add_ellipse(self, row, col):
            self.cell(row, col).ellipse = True
    
        def add_line(self, row, col, *line):
            path = QPainterPath(QPointF(*line[0]) * self.cell_size)
            for point in line[1:]:
                path.lineTo(QPointF(*point) * self.cell_size)
            self.cell(row, col).line = path
    

    SudokuGrid.add_line()*line 参数旨在作为 0-1 范围内的元组作为定义 QPainterPath 的行元素的简单键(类似于渐变的元素 — 0 = 左/上, 0.5 = 中心,1 = 右/下)。

    class MainWindow(QMainWindow):
        def __init__(self, window_w, window_h, orthogonal_intersection_size, cell_size, wide_line_width, narrow_line_width):
            super().__init__()
            self.window_w = window_w
            self.window_h = window_h
            self.orthogonal_intersection_size = orthogonal_intersection_size
            self.cell_size = cell_size
            self.wide_line_width = wide_line_width
            self.narrow_line_width = narrow_line_width
    
            # a sudoku grid is exactly this large. Google a sudoku grid if you dont understand
            self.frame_size = 9 * cell_size + 4 * wide_line_width + 6 * narrow_line_width
            self.initUi()
    
            self.grid.add_line(0, 0, (0.25, 0.5), (1, 0.5))
            self.grid.add_line(0, 1, (0, 0.5), (1, 0.5))
            self.grid.add_line(0, 2, (0, 0.5), (0.5, 0.5), (0.5, 1))
            self.grid.add_line(1, 2, (0.5, 0), (0.5, 1))
            self.grid.add_line(2, 2, (0.5, 0), (0.5, 0.5))
            self.grid.add_ellipse(2, 2)
    

    下面是它在 MainWindow 中的使用方式。当然,您可以以完全不同的方式将线条和椭圆添加到网格中以更适合您的程序,主要目的是展示如何在“小部件之上”实现绘画。

    【讨论】:

    • 这是完美的,非常感谢您的宝贵时间
    猜你喜欢
    • 2018-09-29
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    • 2017-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多