Python 将小部件旋转一定程度
我是pyqt的新手,需要帮助旋转标签。我很困惑,无法理解如何以特定角度旋转整个小部件。不是小部件的内容,而是小部件本身。我正在搜索解决方案,但找不到任何东西。QWidget不支持旋转,但解决方法是将小部件插入QGraphicsProxyWidget并将其添加到QGraphicsCene,然后旋转QGraphicsProxyWidget,该小部件在视觉上产生相同的小部件旋转效果Python 将小部件旋转一定程度,python,pyqt,Python,Pyqt,我是pyqt的新手,需要帮助旋转标签。我很困惑,无法理解如何以特定角度旋转整个小部件。不是小部件的内容,而是小部件本身。我正在搜索解决方案,但找不到任何东西。QWidget不支持旋转,但解决方法是将小部件插入QGraphicsProxyWidget并将其添加到QGraphicsCene,然后旋转QGraphicsProxyWidget,该小部件在视觉上产生相同的小部件旋转效果 从PyQt5导入QtCore、QtGui、qtwidget def main(): 导入系统 app=qtwidts.Q
从PyQt5导入QtCore、QtGui、qtwidget
def main():
导入系统
app=qtwidts.QApplication(sys.argv)
label=qtwidts.QLabel(“堆栈溢出”,alignment=QtCore.Qt.AlignCenter)
graphicsview=QtWidgets.QGraphicsView()
场景=qtwidts.qgraphicscene(graphicsview)
graphicsview.setScene(场景)
proxy=qtwidts.QGraphicsProxyWidget()
proxy.setWidget(标签)
proxy.setTransformOriginPoint(proxy.boundingRect().center())
场景.附加项(代理)
滑块=qtwidts.QSlider(最小值=0,最大值=359,方向=QtCore.Qt.Horizontal)
slider.valueChanged.connect(proxy.setRotation)
label_text=qtwidts.QLabel(
“{}°”.format(slider.value()),alignment=QtCore.Qt.AlignCenter
)
slider.valueChanged.connect(
lambda值:label_text.setText(“{}°”.format(slider.value()))
)
滑块设置值(45)
w=qtwidts.QWidget()
lay=qtwidts.QVBoxLayout(w)
lay.addWidget(graphicsview)
lay.addWidget(滑块)
lay.addWidget(标签\文本)
w、 调整大小(640480)
w、 show()
sys.exit(app.exec_())
如果名称=“\uuuuu main\uuuuuuuu”:
main()
正如@eyllanesc正确解释的那样,Qt中没有“小部件旋转”支持(与大多数标准框架一样) 不过,在你的手上有一些技巧 “简单”标签(不使用
QLabel
)
这就是“简单”的解决方案。既然你说的是一个“标签”,那就可以用一些数学来实现
这种方法的最大优点是大小提示是“简单的”,这意味着它只基于文本内容(如中所示),每当主字体、文本或对齐方式发生更改时,大小提示都会反映这些内容。虽然它支持多行标签,但是如果您需要使用富文本,这种方法的最大问题就出现了;可以使用a来代替标准字符串,但这需要更复杂的大小提示计算实现
from math import radians, sin, cos
from random import randrange
from PyQt5 import QtCore, QtGui, QtWidgets
class AngledLabel(QtWidgets.QWidget):
_alignment = QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop
def __init__(self, text='', angle=0, parent=None):
super(AngledLabel, self).__init__(parent)
self._text = text
self._angle = angle % 360
# keep radians of the current angle *and* its opposite; we're using
# rectangles to get the overall area of the text, and since they use
# right angles, that opposite is angle + 90
self._radians = radians(-angle)
self._radiansOpposite = radians(-angle + 90)
def alignment(self):
return self._alignment
def setAlignment(self, alignment):
# text alignment might affect the text size!
if alignment == self._alignment:
return
self._alignment = alignment
self.setMinimumSize(self.sizeHint())
def angle(self):
return self._angle
def setAngle(self, angle):
# the angle clearly affects the overall size
angle %= 360
if angle == self._angle:
return
self._angle = angle
# update the radians to improve optimization of sizeHint and paintEvent
self._radians = radians(-angle)
self._radiansOpposite = radians(-angle + 90)
self.setMinimumSize(self.sizeHint())
def text(self):
return self._text
def setText(self, text):
if text == self._text:
return
self._text = text
self.setMinimumSize(self.sizeHint())
def sizeHint(self):
# get the bounding rectangle of the text
rect = self.fontMetrics().boundingRect(QtCore.QRect(), self._alignment, self._text)
# use trigonometry to get the actual size of the rotated rectangle
sinWidth = abs(sin(self._radians) * rect.width())
cosWidth = abs(cos(self._radians) * rect.width())
sinHeight = abs(sin(self._radiansOpposite) * rect.height())
cosHeight = abs(cos(self._radiansOpposite) * rect.height())
return QtCore.QSize(cosWidth + cosHeight, sinWidth + sinHeight)
def minimumSizeHint(self):
return self.sizeHint()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
textRect = self.fontMetrics().boundingRect(
QtCore.QRect(), self._alignment, self._text)
width = textRect.width()
height = textRect.height()
# we have to translate the painting rectangle, and that depends on which
# "angle sector" the current angle is
if self._angle <= 90:
deltaX = 0
deltaY = sin(self._radians) * width
elif 90 < self._angle <= 180:
deltaX = cos(self._radians) * width
deltaY = sin(self._radians) * width + sin(self._radiansOpposite) * height
elif 180 < self._angle <= 270:
deltaX = cos(self._radians) * width + cos(self._radiansOpposite) * height
deltaY = sin(self._radiansOpposite) * height
else:
deltaX = cos(self._radiansOpposite) * height
deltaY = 0
qp.translate(.5 - deltaX, .5 - deltaY)
qp.rotate(-self._angle)
qp.drawText(self.rect(), self._alignment, self._text)
class TestWindow(QtWidgets.QWidget):
def __init__(self):
super(TestWindow, self).__init__()
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
self.randomizeButton = QtWidgets.QPushButton('Randomize!')
layout.addWidget(self.randomizeButton, 0, 0, 1, 3)
self.randomizeButton.clicked.connect(self.randomize)
layout.addWidget(QtWidgets.QLabel('Standard label'), 1, 0)
text = 'Some text'
layout.addWidget(QtWidgets.QLabel(text), 1, 2)
self.labels = []
for row, angle in enumerate([randrange(360) for _ in range(8)], 2):
angleLabel = QtWidgets.QLabel(u'{}°'.format(angle))
angleLabel.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
layout.addWidget(angleLabel, row, 0)
label = AngledLabel(text, angle)
layout.addWidget(label, row, 2)
self.labels.append((angleLabel, label))
separator = QtWidgets.QFrame()
separator.setFrameShape(separator.VLine|separator.Sunken)
layout.addWidget(separator, 1, 1, layout.rowCount() - 1, 1)
def randomize(self):
for angleLabel, label in self.labels:
angle = randrange(360)
angleLabel.setText(str(angle))
label.setAngle(angle)
self.adjustSize()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = TestWindow()
w.show()
sys.exit(app.exec_())
正如您所看到的,“随机化”函数有非常不同的结果。虽然第二种方法允许使用更复杂的小部件,但第一种方法对内容更改的反应更好
from random import randrange
from PyQt5 import QtCore, QtGui, QtWidgets
class AngledObject(QtWidgets.QGraphicsView):
_angle = 0
def __init__(self, angle=0, parent=None):
super(AngledObject, self).__init__(parent)
# to prevent the graphics view to draw its borders or background, set the
# FrameShape property to 0 and a transparent background
self.setFrameShape(0)
self.setStyleSheet('background: transparent')
self.setScene(QtWidgets.QGraphicsScene())
# ignore scroll bars!
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
def angle(self):
return self._angle
def setAngle(self, angle):
angle %= 360
if angle == self._angle:
return
self._angle = angle
self._proxy.setTransform(QtGui.QTransform().rotate(-angle))
self.adjustSize()
def resizeEvent(self, event):
super(AngledObject, self).resizeEvent(event)
# ensure that the scene is fully visible after resizing
QtCore.QTimer.singleShot(0, lambda: self.centerOn(self.sceneRect().center()))
def sizeHint(self):
return self.scene().itemsBoundingRect().size().toSize()
def minimumSizeHint(self):
return self.sizeHint()
class AngledLabel(AngledObject):
def __init__(self, text='', angle=0, parent=None):
super(AngledLabel, self).__init__(angle, parent)
self._label = QtWidgets.QLabel(text)
self._proxy = self.scene().addWidget(self._label)
self._label.setStyleSheet('background: transparent')
self.setAngle(angle)
self.alignment = self._label.alignment
def setAlignment(self, alignment):
# text alignment might affect the text size!
if alignment == self._label.alignment():
return
self._label.setAlignment(alignment)
self.setMinimumSize(self.sizeHint())
def text(self):
return self._label.text()
def setText(self, text):
if text == self._label.text():
return
self._label.setText(text)
self.setMinimumSize(self.sizeHint())
class AngledButton(AngledObject):
def __init__(self, text='', angle=0, parent=None):
super(AngledButton, self).__init__(angle, parent)
self._button = QtWidgets.QPushButton(text)
self._proxy = self.scene().addWidget(self._button)
self.setAngle(angle)
class TestWindow(QtWidgets.QWidget):
def __init__(self):
super(TestWindow, self).__init__()
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
self.randomizeButton = QtWidgets.QPushButton('Randomize!')
layout.addWidget(self.randomizeButton, 0, 0, 1, 3)
self.randomizeButton.clicked.connect(self.randomize)
layout.addWidget(QtWidgets.QLabel('Standard label'), 1, 0)
text = 'Some text'
layout.addWidget(QtWidgets.QLabel(text), 1, 2)
self.labels = []
for row, angle in enumerate([randrange(360) for _ in range(4)], 2):
angleLabel = QtWidgets.QLabel(u'{}°'.format(angle))
angleLabel.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
layout.addWidget(angleLabel, row, 0)
label = AngledLabel(text, angle)
layout.addWidget(label, row, 2)
self.labels.append((angleLabel, label))
for row, angle in enumerate([randrange(360) for _ in range(4)], row + 1):
angleLabel = QtWidgets.QLabel(u'{}°'.format(angle))
angleLabel.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
layout.addWidget(angleLabel, row, 0)
label = AngledButton('Button!', angle)
layout.addWidget(label, row, 2)
self.labels.append((angleLabel, label))
separator = QtWidgets.QFrame()
separator.setFrameShape(separator.VLine|separator.Sunken)
layout.addWidget(separator, 1, 1, layout.rowCount() - 1, 1)
def randomize(self):
for angleLabel, label in self.labels:
angle = randrange(360)
angleLabel.setText(str(angle))
label.setAngle(angle)
self.adjustSize()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = TestWindow()
w.show()
sys.exit(app.exec_())