为什么带有自定义绘画的小部件不可见?
Why is the widget with a custom painting not visible?
所以我正在尝试使用自定义小部件构建一个简单的 PyQt 应用程序。然而,画家什么也没画。如果我注释掉第 44-45 行 (label = QLabel('Map');box.addWidget(label)
),我会看到一个大的彩色矩形。但是,当我尝试在矩形上方添加标签时,矩形不再显示。
我想我可能用错了画家,但我不确定。
我是 PyQt 的新手,任何对我的编码风格或逻辑的评论也将不胜感激。
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import (QMainWindow,
QWidget,
QFrame,
QDesktopWidget,
QGridLayout,
QLabel,
QTextEdit,
QSplitter,
QVBoxLayout,
QApplication)
class Simulator(QMainWindow):
def __init__(self):
super().__init__()
self.stdout = QTextEdit()
self.stderr = QTextEdit()
self.exec = QTextEdit()
self.frame = QFrame()
self.setCentralWidget(self.frame)
self.screen = QDesktopWidget().screenGeometry()
self.setGeometry(self.screen)
self.grid = QGridLayout()
self.frame.setLayout(self.grid)
self.map = SimulatedFieldMap()
# -- setting splitters
splitter_r = QSplitter(Qt.Vertical)
splitter_l = QSplitter(Qt.Vertical)
splitter_h = QSplitter(Qt.Horizontal)
splitter_h.addWidget(splitter_l)
splitter_h.addWidget(splitter_r)
# --------------
# -- top left --
frame = QFrame()
box = QVBoxLayout()
frame.setLayout(box)
splitter_l.addWidget(frame)
label = QLabel('Map')
box.addWidget(label)
box.addWidget(self.map)
# ------
# -- bottom left --
box = QVBoxLayout()
frame = QFrame()
frame.setLayout(box)
box.addWidget(QLabel('Exec'))
box.addWidget(self.exec)
splitter_l.addWidget(frame)
# -------
# -- top right --
box = QVBoxLayout()
frame = QFrame()
frame.setLayout(box)
splitter_r.addWidget(frame)
box.addWidget(QLabel('STDOUT'))
box.addWidget(self.stdout)
# -------
# -- bottom right --
box = QVBoxLayout()
frame = QFrame()
frame.setLayout(box)
splitter_r.addWidget(frame)
box.addWidget(QLabel('STDERR'))
box.addWidget(self.stderr)
# -------
self.grid.addWidget(splitter_h, 0, 0)
splitter_h.setSizes((self.screen.width() * 0.7, self.screen.width() * 0.3))
splitter_l.setSizes((self.screen.height() * 0.7, self.screen.height() * 0.3))
splitter_r.setSizes((self.screen.height() * 0.7, self.screen.height() * 0.3))
class SimulatedFieldMap(QWidget):
def __init__(self):
super().__init__()
def paintEvent(self, event):
qp = QPainter()
qp.begin(self)
self.paintMap(qp)
qp.end()
def paintMap(self, qp):
qp.setBrush(QColor(200, 162, 200)) # lilac
qp.setPen(QColor(200, 162, 200))
geo = self.geometry()
qp.drawRect(geo)
if __name__ == '__main__':
app = QApplication([])
app.setStyle('Fusion')
sim = Simulator()
sim.show()
status = app.exec_()
exit(status)
我在 macOS 10.13.5 上使用 Python3.7。
绘制小部件时,使用内部坐标,但geometry()
is coordinates with respect to the parent, so you should not use it, instead you should use rect()
。
from PyQt5 import QtCore, QtGui, QtWidgets
class Simulator(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.stdout = QtWidgets.QTextEdit()
self.stderr = QtWidgets.QTextEdit()
self.exec = QtWidgets.QTextEdit()
self.frame = QtWidgets.QFrame()
self.setCentralWidget(self.frame)
self.grid = QtWidgets.QGridLayout(self.frame)
self.map = SimulatedFieldMap()
# -- setting splitters
splitter_r = QtWidgets.QSplitter(QtCore.Qt.Vertical)
splitter_l = QtWidgets.QSplitter(QtCore.Qt.Vertical)
splitter_h = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
splitter_h.addWidget(splitter_l)
splitter_h.addWidget(splitter_r)
# --------------
# -- top left --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
splitter_l.addWidget(frame)
label = QtWidgets.QLabel('Map')
box.addWidget(label)
box.addWidget(self.map)
# ------
# -- bottom left --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
box.addWidget(QtWidgets.QLabel('Exec'))
box.addWidget(self.exec)
splitter_l.addWidget(frame)
# -------
# -- top right --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
splitter_r.addWidget(frame)
box.addWidget(QtWidgets.QLabel('STDOUT'))
box.addWidget(self.stdout)
# -------
# -- bottom right --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
splitter_r.addWidget(frame)
box.addWidget(QtWidgets.QLabel('STDERR'))
box.addWidget(self.stderr)
# -------
screen = QtWidgets.QDesktopWidget().screenGeometry()
self.grid.addWidget(splitter_h, 0, 0)
splitter_h.setSizes((screen.width() * 0.7, screen.width() * 0.3))
splitter_l.setSizes((screen.height() * 0.7, screen.height() * 0.3))
splitter_r.setSizes((screen.height() * 0.7, screen.height() * 0.3))
class SimulatedFieldMap(QtWidgets.QWidget):
def __init__(self):
super().__init__()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
self.paintMap(qp)
def paintMap(self, qp):
qp.setBrush(QtGui.QColor(200, 162, 200)) # lilac
qp.setPen(QtGui.QColor(200, 162, 200))
qp.drawRect(self.rect())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle('Fusion')
sim = Simulator()
sim.showMaximized()
sys.exit(app.exec_())
更新:如果你想把它放在某个位置,你不应该把它放在布局中,但标签必须是地图的子项并使用move()
建立相对于地图左上角的位置。
# -- top left --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
box.addWidget(self.map)
splitter_l.addWidget(frame)
label = QtWidgets.QLabel('Map', self.map)
label.move(0, 100)
# ------
更新: 问题是QLabel的vertical sizePolicy QSizePolicy::Preferred
导致展开,一个简单的解决办法是改成QSizePolicy::Maximum
,所以根据字体计算出正确的高度。
# -- top left --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
splitter_l.addWidget(frame)
label = QtWidgets.QLabel('Map')
sp = label.sizePolicy()
sp.setVerticalPolicy(QtWidgets.QSizePolicy.Maximum)
label.setSizePolicy(sp)
box.addWidget(label)
box.addWidget(self.map)
# ------
原因
当你在布局中添加一个QLabel
时,它的大小策略默认设置为QSizePolicy.Policy(Preferred)
,它将占据整个space,不留space 为您的小部件 SimulatedFieldMap
。基本上,您的自定义小部件在那里,但它的高度为 0,因此不可见。
灵魂
一种解决方案是通过将标签设置为固定值来限制标签的高度,例如14
。为此,在 label = QLabel('Map')
之后添加 label.setFixedHeight(14)
.
结果
这是这个解决方案的结果:
注意:出于演示目的,我已手动将左侧垂直分隔线向下移动。
所以我正在尝试使用自定义小部件构建一个简单的 PyQt 应用程序。然而,画家什么也没画。如果我注释掉第 44-45 行 (label = QLabel('Map');box.addWidget(label)
),我会看到一个大的彩色矩形。但是,当我尝试在矩形上方添加标签时,矩形不再显示。
我想我可能用错了画家,但我不确定。
我是 PyQt 的新手,任何对我的编码风格或逻辑的评论也将不胜感激。
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import (QMainWindow,
QWidget,
QFrame,
QDesktopWidget,
QGridLayout,
QLabel,
QTextEdit,
QSplitter,
QVBoxLayout,
QApplication)
class Simulator(QMainWindow):
def __init__(self):
super().__init__()
self.stdout = QTextEdit()
self.stderr = QTextEdit()
self.exec = QTextEdit()
self.frame = QFrame()
self.setCentralWidget(self.frame)
self.screen = QDesktopWidget().screenGeometry()
self.setGeometry(self.screen)
self.grid = QGridLayout()
self.frame.setLayout(self.grid)
self.map = SimulatedFieldMap()
# -- setting splitters
splitter_r = QSplitter(Qt.Vertical)
splitter_l = QSplitter(Qt.Vertical)
splitter_h = QSplitter(Qt.Horizontal)
splitter_h.addWidget(splitter_l)
splitter_h.addWidget(splitter_r)
# --------------
# -- top left --
frame = QFrame()
box = QVBoxLayout()
frame.setLayout(box)
splitter_l.addWidget(frame)
label = QLabel('Map')
box.addWidget(label)
box.addWidget(self.map)
# ------
# -- bottom left --
box = QVBoxLayout()
frame = QFrame()
frame.setLayout(box)
box.addWidget(QLabel('Exec'))
box.addWidget(self.exec)
splitter_l.addWidget(frame)
# -------
# -- top right --
box = QVBoxLayout()
frame = QFrame()
frame.setLayout(box)
splitter_r.addWidget(frame)
box.addWidget(QLabel('STDOUT'))
box.addWidget(self.stdout)
# -------
# -- bottom right --
box = QVBoxLayout()
frame = QFrame()
frame.setLayout(box)
splitter_r.addWidget(frame)
box.addWidget(QLabel('STDERR'))
box.addWidget(self.stderr)
# -------
self.grid.addWidget(splitter_h, 0, 0)
splitter_h.setSizes((self.screen.width() * 0.7, self.screen.width() * 0.3))
splitter_l.setSizes((self.screen.height() * 0.7, self.screen.height() * 0.3))
splitter_r.setSizes((self.screen.height() * 0.7, self.screen.height() * 0.3))
class SimulatedFieldMap(QWidget):
def __init__(self):
super().__init__()
def paintEvent(self, event):
qp = QPainter()
qp.begin(self)
self.paintMap(qp)
qp.end()
def paintMap(self, qp):
qp.setBrush(QColor(200, 162, 200)) # lilac
qp.setPen(QColor(200, 162, 200))
geo = self.geometry()
qp.drawRect(geo)
if __name__ == '__main__':
app = QApplication([])
app.setStyle('Fusion')
sim = Simulator()
sim.show()
status = app.exec_()
exit(status)
我在 macOS 10.13.5 上使用 Python3.7。
绘制小部件时,使用内部坐标,但geometry()
is coordinates with respect to the parent, so you should not use it, instead you should use rect()
。
from PyQt5 import QtCore, QtGui, QtWidgets
class Simulator(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.stdout = QtWidgets.QTextEdit()
self.stderr = QtWidgets.QTextEdit()
self.exec = QtWidgets.QTextEdit()
self.frame = QtWidgets.QFrame()
self.setCentralWidget(self.frame)
self.grid = QtWidgets.QGridLayout(self.frame)
self.map = SimulatedFieldMap()
# -- setting splitters
splitter_r = QtWidgets.QSplitter(QtCore.Qt.Vertical)
splitter_l = QtWidgets.QSplitter(QtCore.Qt.Vertical)
splitter_h = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
splitter_h.addWidget(splitter_l)
splitter_h.addWidget(splitter_r)
# --------------
# -- top left --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
splitter_l.addWidget(frame)
label = QtWidgets.QLabel('Map')
box.addWidget(label)
box.addWidget(self.map)
# ------
# -- bottom left --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
box.addWidget(QtWidgets.QLabel('Exec'))
box.addWidget(self.exec)
splitter_l.addWidget(frame)
# -------
# -- top right --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
splitter_r.addWidget(frame)
box.addWidget(QtWidgets.QLabel('STDOUT'))
box.addWidget(self.stdout)
# -------
# -- bottom right --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
splitter_r.addWidget(frame)
box.addWidget(QtWidgets.QLabel('STDERR'))
box.addWidget(self.stderr)
# -------
screen = QtWidgets.QDesktopWidget().screenGeometry()
self.grid.addWidget(splitter_h, 0, 0)
splitter_h.setSizes((screen.width() * 0.7, screen.width() * 0.3))
splitter_l.setSizes((screen.height() * 0.7, screen.height() * 0.3))
splitter_r.setSizes((screen.height() * 0.7, screen.height() * 0.3))
class SimulatedFieldMap(QtWidgets.QWidget):
def __init__(self):
super().__init__()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
self.paintMap(qp)
def paintMap(self, qp):
qp.setBrush(QtGui.QColor(200, 162, 200)) # lilac
qp.setPen(QtGui.QColor(200, 162, 200))
qp.drawRect(self.rect())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle('Fusion')
sim = Simulator()
sim.showMaximized()
sys.exit(app.exec_())
更新:如果你想把它放在某个位置,你不应该把它放在布局中,但标签必须是地图的子项并使用move()
建立相对于地图左上角的位置。
# -- top left --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
box.addWidget(self.map)
splitter_l.addWidget(frame)
label = QtWidgets.QLabel('Map', self.map)
label.move(0, 100)
# ------
更新: 问题是QLabel的vertical sizePolicy QSizePolicy::Preferred
导致展开,一个简单的解决办法是改成QSizePolicy::Maximum
,所以根据字体计算出正确的高度。
# -- top left --
frame = QtWidgets.QFrame()
box = QtWidgets.QVBoxLayout(frame)
splitter_l.addWidget(frame)
label = QtWidgets.QLabel('Map')
sp = label.sizePolicy()
sp.setVerticalPolicy(QtWidgets.QSizePolicy.Maximum)
label.setSizePolicy(sp)
box.addWidget(label)
box.addWidget(self.map)
# ------
原因
当你在布局中添加一个QLabel
时,它的大小策略默认设置为QSizePolicy.Policy(Preferred)
,它将占据整个space,不留space 为您的小部件 SimulatedFieldMap
。基本上,您的自定义小部件在那里,但它的高度为 0,因此不可见。
灵魂
一种解决方案是通过将标签设置为固定值来限制标签的高度,例如14
。为此,在 label = QLabel('Map')
之后添加 label.setFixedHeight(14)
.
结果
这是这个解决方案的结果:
注意:出于演示目的,我已手动将左侧垂直分隔线向下移动。