QFrame在 QDialog中不可见
我在QGIS中使用Windows 11、Python 3.12.12,以及PyQt 5.15.13。不确定这是否是QGIS的特定问题,所以我先在这里试试,而不是去GIS Stack Exchange。
我想给一个 QDialog 增加边框,以把几个布局分隔开来:
layout1 = QVBoxLayout()
layout1.addLayout(sublayout1) #QHBoxLayout
layout1.addLayout(sublayout2) #QHBoxLayout
layout1.addWidget(checkbox1) #QWidget
qframe1 = QFrame()
qframe1.setFrameStyle(QFrame.Panel | QFrame.Plain)
qframe1.setLineWidth(3)
#qframe1.setStyleSheet("background-color: rgb(255,0,0")
qframe1.setLayout(layout1)
self.layout = QVBoxLayout()
#self.layout.addLayout(qframe1) #No exception in my main project
self.layout.addWidget(qframe1)
self.layout.addWidget(checkbox2) #QWidget
self.layout.addSpacing(10)
self.layout.addLayout(layout2) #QHBoxLayout
self.setLayout(self.layout) #self=QDialog
这把所有元素往中心挪动了,因此 QFrame 肯定在那里,但看不见。若我使用 QFrame.Box 也是同样的问题,背景颜色也没帮助让它显现(我宁愿不通过样式表来修复这个)。
我也尝试改用水平线:
qframe1 = QFrame()
qframe1.setFrameStyle(QFrame.HLine | QFrame.Raised)
qframe1.setLineWidth(3)
layout1 = QVBoxLayout()
layout1.addLayout(sublayout1) #QHBoxLayout
layout1.addLayout(sublayout2) #QHBoxLayout
layout1.addWidget(qframe1)
layout1.addWidget(checkbox1) #QWidget
self.layout = QVBoxLayout()
self.layout.addLayout(layout1)
self.layout.addWidget(checkbox2) #QWidget
self.layout.addSpacing(10)
self.layout.addLayout(layout2) #QHBoxLayout
self.setLayout(self.layout)
现在 sublayout2 与 checkbox1 之间的间距变大,但这条线不可见。
我尝试像这里 所建议的那样把 QgsApplication.setStyle("Fusion") 放在顶部——不过,这一切都发生在QGIS插件中的一个 QDialog 里,而不是在常规的独立Windows应用程序中——但它在样式加载时只让QGIS暂时卡住了一秒钟,仍然没有显示 QFrame。
我到底错在哪儿?
编辑:
我现在还尝试了另外两种设置颜色边框的方法,如 这个 问题及其回答所示:
color = QColor(255,0,0)
qframe1.setStyleSheet("border:1px solid %s" % color)
以及
color = QColor(255,0,0)
pal = qframe1.palette()
pal.setColor(QPalette.WindowText, color)
qframe1.setPalette(pal)
结果都是一样的:边框不可见。
最小可复现示例:
from PyQt5.QtWidgets import (QDialog, QHBoxLayout, QVBoxLayout, QCheckBox, QDialogButtonBox, QFrame)
class MyDialog(QDialog):
def __init__(self):
super().__init__()
addborder = True #border or line
layout1 = QHBoxLayout()
checkbox1 = QCheckBox("Checkbox 1")
checkbox2 = QCheckBox("Checkbox 2")
layout1.addWidget(checkbox1)
layout1.addWidget(checkbox2)
qframe1 = QFrame()
if addborder: qframe1.setFrameStyle(QFrame.Panel | QFrame.Plain)
else: qframe1.setFrameStyle(QFrame.HLine | QFrame.Raised)
qframe1.setLineWidth(3)
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
checkbox3 = QCheckBox("Checkbox 3")
self.layout = QVBoxLayout()
if addborder:
qframe1.setLayout(layout1)
self.layout.addWidget(qframe1)
else:
self.layout.addLayout(layout1)
self.layout.addWidget(qframe1)
self.layout.addWidget(checkbox3)
self.layout.addSpacing(10)
self.layout.addWidget(button_box)
self.setLayout(self.layout)
md = MyDialog()
md.exec()
使用 addborder = True 的结果:
解决方案
QGIS内置的两个UI主题(“Blend of Gray”和“Night Mapping”)是 Qt Stylesheets(QSS),其中包含针对 QFrame 和 QGroupBox 小部件的规则,这些规则会隐藏边框。具体来说,QGIS的源代码 中包含这两条规则:
QGroupBox {
padding-top: 1.05em;
border:1px solid rgba(0,0,0,0);
}
QFrame, QFrame:disabled {
border:1px solid rgba(0,0,0,0);
}
它们使边框变得透明(因为颜色的alpha分量被设为零)。这似乎表明隐藏边框是QGIS的一种有意设计选择。
如果UI主题被设置为 "default",QGIS会回退到Qt的小部件样式,这种样式根本不使用QSS。在这种配置下,无法确定边框是否会显示——内置的 "Fusion" 小部件样式会显示边框,但显然也有其他Qt小部件样式不会。
因此,一个简单的解决方法是覆盖上述两条规则,并将 border-color属性 替换为更合适的值。例如,像下面这样:
qframe1.setStyleSheet('QFrame, QFrame:disabled {border: 1px solid grey}')
然而,如果使用的是Qt小部件样式(而不是某个QSS主题),这类修复可能会产生不可预知的结果。因此,理想情况下,只有在使用QSS主题时才应该应用它。(大概,[QApplication.instance().styleSheet()] 应在这种情况下返回非空字符串——但我自己无法亲自测试)。
