QSS 不适用于 QVBoxLayout 中(复合小部件)中的第一个 QSizeGrip

Posted

技术标签:

【中文标题】QSS 不适用于 QVBoxLayout 中(复合小部件)中的第一个 QSizeGrip【英文标题】:QSS not applied to first QSizeGrip in (composite widget) in QVBoxLayout 【发布时间】:2019-03-19 22:53:24 【问题描述】:

如下所示,小部件TestWidget 包含一个QFrame 和一个CSS 样式的QSizeGrip。几个TestWidget 实例放在一个QVBoxLayout

from PySide import QtGui, QtCore
import sys

class TestWidget(QtGui.QWidget):
    def __init__(self , parent=None):
        super(TestWidget , self).__init__(parent)

        layout = QtGui.QVBoxLayout()
        layout.setContentsMargins( 0 , 0 , 0 , 0 )

        frame = QtGui.QFrame()
        frame.setFrameShape(QtGui.QFrame.StyledPanel)
        frame.setMinimumHeight( 100 )

        grip = QtGui.QSizeGrip(self)
        grip.setStyleSheet( "QSizeGrip  image: url(dots.png); ")
        grip.setCursor(QtCore.Qt.SplitVCursor)

        layout.addWidget(frame)
        layout.addWidget( grip , 0 , QtCore.Qt.AlignBottom | QtCore.Qt.AlignRight )

        self.setLayout(layout)

class TestApp(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(TestApp, self).__init__(parent)

        track1 = TestWidget()
        track2 = TestWidget()
        track3 = TestWidget()

        centralWidget = QtGui.QWidget()
        layout = QtGui.QVBoxLayout(centralWidget)

        layout.addWidget(track1)
        layout.addWidget(track2)
        layout.addWidget(track3)

        self.setCentralWidget(centralWidget)
        self.show() 

if __name__=="__main__":
    app=QtGui.QApplication(sys.argv)
    myapp = TestApp();
    sys.exit(app.exec_())   

如下所示,QVBoxLayout 中第一个 TestWidget 的大小夹点仅在 TestWidget 是布局中的唯一元素时出现。

Qt 版本 4.8.7

PySide 1.2.2 版


程序的PySide2版本(下)有同样的问题

from PySide2 import QtCore
from PySide2.QtWidgets import QApplication, QWidget , QMainWindow , QGraphicsView , QVBoxLayout , QFrame , QSizeGrip , QWidget

import sys

class TestWidget(QWidget):
    def __init__(self , parent=None):
        super(TestWidget , self).__init__(parent)

        layout = QVBoxLayout()
        layout.setContentsMargins( 0 , 0 , 0 , 0 )

        frame = QFrame()
        frame.setFrameShape(QFrame.StyledPanel)
        frame.setMinimumHeight( 100 )

        grip = QSizeGrip(self)
        grip.setStyleSheet( "QSizeGrip  image: url(dots.png); ")
        grip.setCursor(QtCore.Qt.SplitVCursor)

        layout.addWidget(frame)
        layout.addWidget( grip , 0 , QtCore.Qt.AlignBottom | QtCore.Qt.AlignRight )

        self.setLayout(layout)

class TestApp(QMainWindow):
    def __init__(self, parent=None):
        super(TestApp, self).__init__(parent)

        track1 = TestWidget()
        track2 = TestWidget()
        track3 = TestWidget()

        centralWidget = QWidget()
        layout = QVBoxLayout(centralWidget)

        layout.addWidget(track1)
        layout.addWidget(track2)
        layout.addWidget(track3)

        self.setCentralWidget(centralWidget)
        self.show() 

if __name__=="__main__":
    app = QApplication(sys.argv)
    myapp = TestApp();
    sys.exit(app.exec_())   

PySide2 版本 5.12.1

【问题讨论】:

@eyllanesc 对不起,我最近被淹没了。顺便说一句,我已将此报告为 PySide/PySide2 错误 bugreports.qt.io/browse/PYSIDE-978 但这不是错误,错误是行为没有记录 @eyllanesc 我明白你的意思。该行为已被编码,但 IMO 它违反了principle of least astonishment 好的,根据这个原则,报告应该在 Qt 中,而不是在 PySide2 中。 :-) 此问题已被标记为 Qt 错误。 【参考方案1】:

观察到的是预先确定的行为,但没有记录在案,如果 source code 被修改,它将被观察到:

Qt::Corner QSizeGripPrivate::corner() const

    Q_Q(const QSizeGrip);
    QWidget *tlw = qt_sizegrip_topLevelWidget(const_cast<QSizeGrip *>(q));
    const QPoint sizeGripPos = q->mapTo(tlw, QPoint(0, 0));
    bool isAtBottom = sizeGripPos.y() >= tlw->height() / 2;
    bool isAtLeft = sizeGripPos.x() <= tlw->width() / 2;
    if (isAtLeft)
        return isAtBottom ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
    else
        return isAtBottom ? Qt::BottomRightCorner : Qt::TopRightCorner;

在哪里观察到,如果sizeGrip在窗口的上半部分,它就放在上半部分,这就是它观察到的行为的原因。

解决方法是覆盖QSizeGrip的paintEvent方法:

PySide2:

import sys
from PySide2 import QtCore, QtGui, QtWidgets

class SizeGrip(QtWidgets.QSizeGrip):
    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        opt = QtWidgets.QStyleOptionSizeGrip()
        opt.initFrom(self)
        opt.corner = QtCore.Qt.BottomRightCorner
        self.style().drawControl(QtWidgets.QStyle.CE_SizeGrip, opt, painter, self)

class TestWidget(QtWidgets.QWidget):
    def __init__(self , parent=None):
        super(TestWidget , self).__init__(parent)
        layout = QtWidgets.QVBoxLayout(self)
        layout.setContentsMargins( 0 , 0 , 0 , 0 )
        frame = QtWidgets.QFrame()
        frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        frame.setMinimumHeight( 100 )
        grip = SizeGrip(self)
        grip.setStyleSheet('''QSizeGrip  
            image: url(dots.png);
        ''')
        layout.addWidget(frame)
        layout.addWidget(grip , 0 , QtCore.Qt.AlignBottom | QtCore.Qt.AlignRight )

class TestApp(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(TestApp, self).__init__(parent)
        centralWidget = QtWidgets.QWidget()
        layout = QtWidgets.QVBoxLayout(centralWidget)
        for _ in range(3):
            layout.addWidget(TestWidget())
        self.setCentralWidget(centralWidget)
        self.show()

if __name__=="__main__":
    app = QtWidgets.QApplication(sys.argv)
    myapp = TestApp();
    sys.exit(app.exec_())   

PySide:

import sys
from PySide import QtCore, QtGui

class SizeGrip(QtGui.QSizeGrip):
    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        opt = QtGui.QStyleOptionSizeGrip()
        opt.initFrom(self)
        opt.corner = QtCore.Qt.BottomRightCorner
        self.style().drawControl(QtGui.QStyle.CE_SizeGrip, opt, painter, self)

class TestWidget(QtGui.QWidget):
    def __init__(self , parent=None):
        super(TestWidget , self).__init__(parent)
        layout = QtGui.QVBoxLayout(self)
        layout.setContentsMargins( 0 , 0 , 0 , 0 )
        frame = QtGui.QFrame()
        frame.setFrameShape(QtGui.QFrame.StyledPanel)
        frame.setMinimumHeight( 100 )
        grip = SizeGrip(self)
        grip.setStyleSheet('''QSizeGrip  
            image: url(dots.png);
        ''')
        grip.setCursor(QtCore.Qt.SplitVCursor)
        layout.addWidget(frame)
        layout.addWidget(grip , 0 , QtCore.Qt.AlignBottom | QtCore.Qt.AlignRight )

class TestApp(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(TestApp, self).__init__(parent)
        centralWidget = QtGui.QWidget()
        layout = QtGui.QVBoxLayout(centralWidget)
        for _ in range(3):
            layout.addWidget(TestWidget())
        self.setCentralWidget(centralWidget)
        self.show()

if __name__=="__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = TestApp();
    sys.exit(app.exec_())   

【讨论】:

以上是关于QSS 不适用于 QVBoxLayout 中(复合小部件)中的第一个 QSizeGrip的主要内容,如果未能解决你的问题,请参考以下文章

Spring Batch分区不适用于复合项目处理器

播放 1.2.4 findByID 不适用于复合 ID

Fetch Lazy 不适用于具有复合 PK 的 ManyToOne

Qt常用QSS集合

为自定义 QWidget 设置背景颜色

Android 数据绑定不适用于 <merge> 属性