简体   繁体   English

PyQt5:为什么QGroupBox的默认样式在绘画时会消失?

[英]PyQt5: Why does the default style of a QGroupBox disappear when painting?

My application uses a variety of QGroupBoxes arranged in a QGridLayout to display information.我的应用程序使用排列在 QGridLayout 中的各种 QGroupBox 来显示信息。 I would like one of them to display custom shapes that I draw.我希望其中一个显示我绘制的自定义形状。 Since I want the shapes to be it's GroupBox and it's difficult and not good to get the widget's position relative to the window, I decided to subclass the GroupBox and add the paint event to the subclass.由于我希望形状是 GroupBox 并且很难获得小部件相对于窗口的位置,因此我决定将 GroupBox 子类化并将绘制事件添加到子类中。 This works beautifully, however it completely eliminates the default style of the GroupBox, including the title.这很好用,但是它完全消除了 GroupBox 的默认样式,包括标题。

The following code creates a simple window with two GroupBoxes, one using the standard class and one with the paint event in a sub class.下面的代码创建了一个带有两个 GroupBox 的简单窗口,一个使用标准类,一个使用子类中的绘制事件。 You should see that the one on the right only has the painted rectangle and none of the GroupBox style.您应该看到右边的只有绘制的矩形,没有 GroupBox 样式。

If you comment out the paint event, then the GroupBox displays as usual.如果您注释掉绘制事件,则 GroupBox 将照常显示。 Why is this happening and what should I do to keep the GroupBox style?为什么会发生这种情况,我应该怎么做才能保持 GroupBox 风格? Is there another way to use the painter within a GroupBox that doesn't use a paint event in a subclass?是否有另一种方法可以在不使用子类中的绘制事件的 GroupBox 中使用画家?

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *


class MainWindow(QWidget):
    def __init__(self, *args, **kwargs):
        ## Set up Window layout
        super(MainWindow, self).__init__(*args, **kwargs)

        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.setGeometry(300, 50, 1000, 700)

        leftGroup = QGroupBox('Left Group')
        self.layout.addWidget(leftGroup, 0, 0)

        rightGroup = RightGroup()
        self.layout.addWidget(rightGroup, 0, 1)


class RightGroup(QGroupBox):
    def __init__(self):
        super(RightGroup, self).__init__('Right Group')

    def paintEvent(self, event):
        painter = QPainter(self)

        # Paint Style
        painter.setPen(QPen(Qt.black, .5, Qt.SolidLine))

        painter.drawRect(QRectF(0, 0, self.width(), self.height()))


if __name__ == '__main__':
    ## Creates a QT Application
    app = QApplication([])

    ## Creates a window
    window = MainWindow()

    ## Shows the window
    window.show()
    app.exec_()

Window with paint event enabled启用绘制事件的窗口

Window with paint event commented out带有绘制事件的窗口被注释掉

You are overriding the whole painting, and "overriding" means that you ignore the default behavior.您正在覆盖整个绘画,“覆盖”意味着您忽略默认行为。

The default behavior of paintEvent() of a standard Qt widget results in painting that widget (whether it's a button, a label, a groupbox, etc.).标准 Qt 小部件的paintEvent()的默认行为导致绘制该小部件(无论是按钮、标签、组框等)。 If you just override that, that widget will never be painted, unless you call the default implementation (ie: super().paintEvent(event) ).如果您只是覆盖它,则该小部件将永远不会被绘制,除非您调用默认实现(即: super().paintEvent(event) )。

For instance, consider the following:例如,考虑以下情况:

class RightGroup(QGroupBox):
    pass

which will properly show a normal group box.这将正确显示一个正常的组框。

Now, this:现在,这个:

class RightGroup(QGroupBox):
    def paintEvent(self, event):
        pass

will show nothing, as you're explicitly ignoring the default painting behavior.不会显示任何内容,因为您明确忽略了默认绘画行为。 To "revert" this and also add custom drawing:要“恢复”它添加自定义绘图:

class RightGroup(QGroupBox):
    def paintEvent(self, event):
        super().paintEvent(event)
        qp = QPainter(self)
        qp.drawRect(self.rect().adjusted(0, 0, -1, -1)

Note: unless you use antialiasing and the pen width is smaller or equal than 0.5, you should always restrict the right/bottom edges of the object rectangle to an integer smaller or equal to half of the pen width.注意:除非您使用抗锯齿且笔宽小于或等于 0.5,否则应始终将对象矩形的右/下边缘限制为小于或等于笔宽一半的整数。 That's the reason of the adjusted(0, 0, -1, -1) above.这就是上面adjusted(0, 0, -1, -1)的原因。 For your above code, it would have been QRectF(0, 0, self.width() - 1, self.height() - 1)对于您上面的代码,它将是QRectF(0, 0, self.width() - 1, self.height() - 1)

That said, you probably want to draw inside that groupbox, so, a better solution would be to create a subclass for the contents of that groupbox, add that to the groupbox and override the paint event of that subclass instead.也就是说,您可能希望该组框内绘图,因此,更好的解决方案是为该组框的内容创建一个子类,将其添加到该组框并覆盖该子类的绘制事件。

class MainWindow(QWidget):
    def __init__(self, *args, **kwargs):
        # ...
        rightGroup = QGroupBox('Right Group')
        self.layout.addWidget(rightGroup, 0, 1)

        rightLayout = QVBoxLayout(rightGroup)
        rightLayout.addWidget(Canvas())


class Canvas(QWidget):
    def __init__(self):
        super().__init__()
        self.path = QPainterPath()

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.path.moveTo(event.pos())

    def mouseMoveEvent(self, event):
        # note: here we use "buttons", not "button".
        if event.buttons() == Qt.LeftButton:
            self.path.lineTo(event.pos())
            self.update()

    def mouseReleaseEvent(self, event):
        self.update()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHints(painter.Antialiasing)

        painter.setPen(QPen(Qt.black, .5, Qt.SolidLine))

        painter.drawRect(self.rect())

        painter.drawPath(self.path)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM