如何在 qt 设计器中创建拨动开关按钮?
How to create toggle switch button in qt designer?
我正在尝试在 qt 设计器中创建切换按钮。我也在互联网上参考,但我找不到该怎么做。任何人都可以知道如何做切换开关按钮。我附上了一个示例按钮图像。
编辑
我在下面创建了一个文本框,我想要这个切换按钮。当我尝试添加时,它会抛出错误。如何在文本区域下方添加按钮?我也附上了代码片段。
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QPropertyAnimation, QRectF, QSize, Qt, pyqtProperty
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import (
QAbstractButton,
QApplication,
QHBoxLayout,
QSizePolicy,
QWidget,
)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(472, 180)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.url = QtWidgets.QLineEdit(self.centralwidget)
self.url.setGeometry(QtCore.QRect(30, 20, 411, 31))
font = QtGui.QFont()
font.setFamily("MS Shell Dlg 2")
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.url.setFont(font)
self.url.setAutoFillBackground(False)
self.url.setStyleSheet("border-radius:10px;")
self.url.setAlignment(QtCore.Qt.AlignCenter)
self.url.setCursorMoveStyle(QtCore.Qt.LogicalMoveStyle)
self.url.setObjectName("url")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 472, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.url.setPlaceholderText(_translate("MainWindow", "Playlist URL"))
if __name__ == "__main__":
import sys
main()
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Qt Designer 在 window 中设置小部件的位置并初始化一些属性,但是创建带有自定义绘制的小部件作为开关不起作用,因此您必须使用 python.很久以前,一个项目实现了一个 switch,所以它会显示代码:
from PyQt5.QtCore import QObject, QSize, QPointF, QPropertyAnimation, QEasingCurve, pyqtProperty, pyqtSlot, Qt
from PyQt5.QtGui import QPainter, QPalette, QLinearGradient, QGradient
from PyQt5.QtWidgets import QAbstractButton, QApplication, QWidget, QHBoxLayout, QLabel
class SwitchPrivate(QObject):
def __init__(self, q, parent=None):
QObject.__init__(self, parent=parent)
self.mPointer = q
self.mPosition = 0.0
self.mGradient = QLinearGradient()
self.mGradient.setSpread(QGradient.PadSpread)
self.animation = QPropertyAnimation(self)
self.animation.setTargetObject(self)
self.animation.setPropertyName(b'position')
self.animation.setStartValue(0.0)
self.animation.setEndValue(1.0)
self.animation.setDuration(200)
self.animation.setEasingCurve(QEasingCurve.InOutExpo)
self.animation.finished.connect(self.mPointer.update)
@pyqtProperty(float)
def position(self):
return self.mPosition
@position.setter
def position(self, value):
self.mPosition = value
self.mPointer.update()
def draw(self, painter):
r = self.mPointer.rect()
margin = r.height()/10
shadow = self.mPointer.palette().color(QPalette.Dark)
light = self.mPointer.palette().color(QPalette.Light)
button = self.mPointer.palette().color(QPalette.Button)
painter.setPen(Qt.NoPen)
self.mGradient.setColorAt(0, shadow.darker(130))
self.mGradient.setColorAt(1, light.darker(130))
self.mGradient.setStart(0, r.height())
self.mGradient.setFinalStop(0, 0)
painter.setBrush(self.mGradient)
painter.drawRoundedRect(r, r.height()/2, r.height()/2)
self.mGradient.setColorAt(0, shadow.darker(140))
self.mGradient.setColorAt(1, light.darker(160))
self.mGradient.setStart(0, 0)
self.mGradient.setFinalStop(0, r.height())
painter.setBrush(self.mGradient)
painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), r.height()/2, r.height()/2)
self.mGradient.setColorAt(0, button.darker(130))
self.mGradient.setColorAt(1, button)
painter.setBrush(self.mGradient)
x = r.height()/2.0 + self.mPosition*(r.width()-r.height())
painter.drawEllipse(QPointF(x, r.height()/2), r.height()/2-margin, r.height()/2-margin)
@pyqtSlot(bool, name='animate')
def animate(self, checked):
self.animation.setDirection(QPropertyAnimation.Forward if checked else QPropertyAnimation.Backward)
self.animation.start()
class Switch(QAbstractButton):
def __init__(self, parent=None):
QAbstractButton.__init__(self, parent=parent)
self.dPtr = SwitchPrivate(self)
self.setCheckable(True)
self.clicked.connect(self.dPtr.animate)
def sizeHint(self):
return QSize(84, 42)
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
self.dPtr.draw(painter)
def resizeEvent(self, event):
self.update()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Switch()
w.show()
sys.exit(app.exec_())
注意: Qt 世界中的另一种可能的解决方案是使用 QML Switch1, 2 组件。
注意: 在 中,我指出了如何将自定义小部件添加到 .ui 文件。
一个可能的解决方案是使用带有 QCheckBox 的样式表。只需使用以下代码编辑复选框的样式表:
QCheckBox::indicator:unchecked {
image: url(switch_off.png);
}
QCheckBox::indicator:checked {
image: url(switch_on.png);
}
最小 运行 示例:
from PyQt5 import QtWidgets
import sys
app = QtWidgets.QApplication(sys.argv)
switch = QtWidgets.QCheckBox()
switch.setStyleSheet('''
QCheckBox::indicator:unchecked {
image: url(switch_off.png);
}
QCheckBox::indicator:checked {
image: url(switch_on.png);
}
''')
switch.show()
sys.exit(app.exec_())
不幸的是,如果您需要调整大小并希望复选框调整其外观,这并不总是有效。
唯一的选择是子类化 QPushButton/QAbstractButton(checkable()
属性 设置为 True
)并自己实现 paintEvent
,正如 .
已经建议的那样
qtwidgets
python 模块 (pip install qtwidgets
) 中给出了一个相当简单的 Toggle Switch 实现。就我而言,我需要一个更个性化的版本来适应我的整体应用程序设计。
代码非常简单。我根据原作者的 git hub repo 创建了一个修改后的切换开关小部件:
Martin Fitzpatrick: qtwidgets/toggle。这里真正的功劳归功于 M. Fitzpatrick。这是我的修改(添加了“ON”文本,设置垂直和水平缩放比例,一些默认颜色):
class ToggleSwitch(QCheckBox):
_transparent_pen = QPen(Qt.transparent)
_light_grey_pen = QPen(Qt.lightGray)
_black_pen = QPen(Qt.black)
def __init__(self,
parent=None,
bar_color=Qt.gray,
checked_color="#00B0FF",
handle_color=Qt.white,
h_scale=1.0,
v_scale=1.0,
fontSize=10):
super().__init__(parent)
# Save our properties on the object via self, so we can access them later
# in the paintEvent.
self._bar_brush = QBrush(bar_color)
self._bar_checked_brush = QBrush(QColor(checked_color).lighter())
self._handle_brush = QBrush(handle_color)
self._handle_checked_brush = QBrush(QColor(checked_color))
# Setup the rest of the widget.
self.setContentsMargins(8, 0, 8, 0)
self._handle_position = 0
self._h_scale = h_scale
self._v_scale = v_scale
self._fontSize = fontSize
self.stateChanged.connect(self.handle_state_change)
def sizeHint(self):
return QSize(58, 45)
def hitButton(self, pos: QPoint):
return self.contentsRect().contains(pos)
def paintEvent(self, e: QPaintEvent):
contRect = self.contentsRect()
width = contRect.width() * self._h_scale
height = contRect.height() * self._v_scale
handleRadius = round(0.24 * height)
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing)
p.setPen(self._transparent_pen)
barRect = QRectF(0, 0, width - handleRadius, 0.40 * height)
barRect.moveCenter(contRect.center())
rounding = barRect.height() / 2
# the handle will move along this line
trailLength = contRect.width()*self._h_scale - 2 * handleRadius
xLeft = contRect.center().x() - (trailLength + handleRadius)/2
xPos = xLeft + handleRadius + trailLength * self._handle_position
if self.isChecked():
p.setBrush(self._bar_checked_brush)
p.drawRoundedRect(barRect, rounding, rounding)
p.setBrush(self._handle_checked_brush)
p.setPen(self._black_pen)
p.setFont(QFont('Helvetica', self._fontSize, 75))
p.drawText(xLeft + handleRadius / 2, contRect.center().y() +
handleRadius / 2,"ON")
else:
p.setBrush(self._bar_brush)
p.drawRoundedRect(barRect, rounding, rounding)
p.setPen(self._light_grey_pen)
p.setBrush(self._handle_brush)
p.setPen(self._light_grey_pen)
p.drawEllipse(
QPointF(xPos, barRect.center().y()),
handleRadius, handleRadius)
p.end()
@Slot(int)
def handle_state_change(self, value):
self._handle_position = 1 if value else 0
@Property(float)
def handle_position(self):
return self._handle_position
@handle_position.setter
def handle_position(self, pos):
"""change the property
we need to trigger QWidget.update() method, either by:
1- calling it here [ what we're doing ].
2- connecting the QPropertyAnimation.valueChanged() signal to it.
"""
self._handle_position = pos
self.update()
def setH_scale(self,value):
self._h_scale = value
self.update()
def setV_scale(self,value):
self._v_scale = value
self.update()
def setFontSize(self,value):
self._fontSize = value
self.update()
我正在尝试在 qt 设计器中创建切换按钮。我也在互联网上参考,但我找不到该怎么做。任何人都可以知道如何做切换开关按钮。我附上了一个示例按钮图像。
编辑
我在下面创建了一个文本框,我想要这个切换按钮。当我尝试添加时,它会抛出错误。如何在文本区域下方添加按钮?我也附上了代码片段。
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QPropertyAnimation, QRectF, QSize, Qt, pyqtProperty
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import (
QAbstractButton,
QApplication,
QHBoxLayout,
QSizePolicy,
QWidget,
)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(472, 180)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.url = QtWidgets.QLineEdit(self.centralwidget)
self.url.setGeometry(QtCore.QRect(30, 20, 411, 31))
font = QtGui.QFont()
font.setFamily("MS Shell Dlg 2")
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.url.setFont(font)
self.url.setAutoFillBackground(False)
self.url.setStyleSheet("border-radius:10px;")
self.url.setAlignment(QtCore.Qt.AlignCenter)
self.url.setCursorMoveStyle(QtCore.Qt.LogicalMoveStyle)
self.url.setObjectName("url")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 472, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.url.setPlaceholderText(_translate("MainWindow", "Playlist URL"))
if __name__ == "__main__":
import sys
main()
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Qt Designer 在 window 中设置小部件的位置并初始化一些属性,但是创建带有自定义绘制的小部件作为开关不起作用,因此您必须使用 python.很久以前,一个项目实现了一个 switch,所以它会显示代码:
from PyQt5.QtCore import QObject, QSize, QPointF, QPropertyAnimation, QEasingCurve, pyqtProperty, pyqtSlot, Qt
from PyQt5.QtGui import QPainter, QPalette, QLinearGradient, QGradient
from PyQt5.QtWidgets import QAbstractButton, QApplication, QWidget, QHBoxLayout, QLabel
class SwitchPrivate(QObject):
def __init__(self, q, parent=None):
QObject.__init__(self, parent=parent)
self.mPointer = q
self.mPosition = 0.0
self.mGradient = QLinearGradient()
self.mGradient.setSpread(QGradient.PadSpread)
self.animation = QPropertyAnimation(self)
self.animation.setTargetObject(self)
self.animation.setPropertyName(b'position')
self.animation.setStartValue(0.0)
self.animation.setEndValue(1.0)
self.animation.setDuration(200)
self.animation.setEasingCurve(QEasingCurve.InOutExpo)
self.animation.finished.connect(self.mPointer.update)
@pyqtProperty(float)
def position(self):
return self.mPosition
@position.setter
def position(self, value):
self.mPosition = value
self.mPointer.update()
def draw(self, painter):
r = self.mPointer.rect()
margin = r.height()/10
shadow = self.mPointer.palette().color(QPalette.Dark)
light = self.mPointer.palette().color(QPalette.Light)
button = self.mPointer.palette().color(QPalette.Button)
painter.setPen(Qt.NoPen)
self.mGradient.setColorAt(0, shadow.darker(130))
self.mGradient.setColorAt(1, light.darker(130))
self.mGradient.setStart(0, r.height())
self.mGradient.setFinalStop(0, 0)
painter.setBrush(self.mGradient)
painter.drawRoundedRect(r, r.height()/2, r.height()/2)
self.mGradient.setColorAt(0, shadow.darker(140))
self.mGradient.setColorAt(1, light.darker(160))
self.mGradient.setStart(0, 0)
self.mGradient.setFinalStop(0, r.height())
painter.setBrush(self.mGradient)
painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), r.height()/2, r.height()/2)
self.mGradient.setColorAt(0, button.darker(130))
self.mGradient.setColorAt(1, button)
painter.setBrush(self.mGradient)
x = r.height()/2.0 + self.mPosition*(r.width()-r.height())
painter.drawEllipse(QPointF(x, r.height()/2), r.height()/2-margin, r.height()/2-margin)
@pyqtSlot(bool, name='animate')
def animate(self, checked):
self.animation.setDirection(QPropertyAnimation.Forward if checked else QPropertyAnimation.Backward)
self.animation.start()
class Switch(QAbstractButton):
def __init__(self, parent=None):
QAbstractButton.__init__(self, parent=parent)
self.dPtr = SwitchPrivate(self)
self.setCheckable(True)
self.clicked.connect(self.dPtr.animate)
def sizeHint(self):
return QSize(84, 42)
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
self.dPtr.draw(painter)
def resizeEvent(self, event):
self.update()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Switch()
w.show()
sys.exit(app.exec_())
注意: Qt 世界中的另一种可能的解决方案是使用 QML Switch1, 2 组件。
注意: 在
一个可能的解决方案是使用带有 QCheckBox 的样式表。只需使用以下代码编辑复选框的样式表:
QCheckBox::indicator:unchecked {
image: url(switch_off.png);
}
QCheckBox::indicator:checked {
image: url(switch_on.png);
}
最小 运行 示例:
from PyQt5 import QtWidgets
import sys
app = QtWidgets.QApplication(sys.argv)
switch = QtWidgets.QCheckBox()
switch.setStyleSheet('''
QCheckBox::indicator:unchecked {
image: url(switch_off.png);
}
QCheckBox::indicator:checked {
image: url(switch_on.png);
}
''')
switch.show()
sys.exit(app.exec_())
不幸的是,如果您需要调整大小并希望复选框调整其外观,这并不总是有效。
唯一的选择是子类化 QPushButton/QAbstractButton(checkable()
属性 设置为 True
)并自己实现 paintEvent
,正如
qtwidgets
python 模块 (pip install qtwidgets
) 中给出了一个相当简单的 Toggle Switch 实现。就我而言,我需要一个更个性化的版本来适应我的整体应用程序设计。
代码非常简单。我根据原作者的 git hub repo 创建了一个修改后的切换开关小部件: Martin Fitzpatrick: qtwidgets/toggle。这里真正的功劳归功于 M. Fitzpatrick。这是我的修改(添加了“ON”文本,设置垂直和水平缩放比例,一些默认颜色):
class ToggleSwitch(QCheckBox):
_transparent_pen = QPen(Qt.transparent)
_light_grey_pen = QPen(Qt.lightGray)
_black_pen = QPen(Qt.black)
def __init__(self,
parent=None,
bar_color=Qt.gray,
checked_color="#00B0FF",
handle_color=Qt.white,
h_scale=1.0,
v_scale=1.0,
fontSize=10):
super().__init__(parent)
# Save our properties on the object via self, so we can access them later
# in the paintEvent.
self._bar_brush = QBrush(bar_color)
self._bar_checked_brush = QBrush(QColor(checked_color).lighter())
self._handle_brush = QBrush(handle_color)
self._handle_checked_brush = QBrush(QColor(checked_color))
# Setup the rest of the widget.
self.setContentsMargins(8, 0, 8, 0)
self._handle_position = 0
self._h_scale = h_scale
self._v_scale = v_scale
self._fontSize = fontSize
self.stateChanged.connect(self.handle_state_change)
def sizeHint(self):
return QSize(58, 45)
def hitButton(self, pos: QPoint):
return self.contentsRect().contains(pos)
def paintEvent(self, e: QPaintEvent):
contRect = self.contentsRect()
width = contRect.width() * self._h_scale
height = contRect.height() * self._v_scale
handleRadius = round(0.24 * height)
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing)
p.setPen(self._transparent_pen)
barRect = QRectF(0, 0, width - handleRadius, 0.40 * height)
barRect.moveCenter(contRect.center())
rounding = barRect.height() / 2
# the handle will move along this line
trailLength = contRect.width()*self._h_scale - 2 * handleRadius
xLeft = contRect.center().x() - (trailLength + handleRadius)/2
xPos = xLeft + handleRadius + trailLength * self._handle_position
if self.isChecked():
p.setBrush(self._bar_checked_brush)
p.drawRoundedRect(barRect, rounding, rounding)
p.setBrush(self._handle_checked_brush)
p.setPen(self._black_pen)
p.setFont(QFont('Helvetica', self._fontSize, 75))
p.drawText(xLeft + handleRadius / 2, contRect.center().y() +
handleRadius / 2,"ON")
else:
p.setBrush(self._bar_brush)
p.drawRoundedRect(barRect, rounding, rounding)
p.setPen(self._light_grey_pen)
p.setBrush(self._handle_brush)
p.setPen(self._light_grey_pen)
p.drawEllipse(
QPointF(xPos, barRect.center().y()),
handleRadius, handleRadius)
p.end()
@Slot(int)
def handle_state_change(self, value):
self._handle_position = 1 if value else 0
@Property(float)
def handle_position(self):
return self._handle_position
@handle_position.setter
def handle_position(self, pos):
"""change the property
we need to trigger QWidget.update() method, either by:
1- calling it here [ what we're doing ].
2- connecting the QPropertyAnimation.valueChanged() signal to it.
"""
self._handle_position = pos
self.update()
def setH_scale(self,value):
self._h_scale = value
self.update()
def setV_scale(self,value):
self._v_scale = value
self.update()
def setFontSize(self,value):
self._fontSize = value
self.update()