pyqt中的切换按钮

Posted

技术标签:

【中文标题】pyqt中的切换按钮【英文标题】:Switch button in pyqt 【发布时间】:2019-06-28 12:30:22 【问题描述】:

是否可以用pyqt5创建开关按钮?

我正在使用 pyqt5 在 python 中设计一个过滤工具。用户应该能够对某些数据应用过滤器或其补充,甚至可以组合过滤器。

我在 qtablewidget 中显示可能的过滤器,用户可以使用复选框选择过滤器来应用。在每一行中,复选框是独占的,即用户不能同时选择过滤器及其补充。

但问题是,一旦我们连续选择了一个复选框,我们就不能取消选择它,除非我们选择它的反面。 事实上,当过滤器刚刚加载时,所有的框都未选中(它们在某种程度上是空的)所以我可以选择要应用的过滤器但是当我想选择另一个过滤器时,先例过滤器的一个框仍然选中(选中过滤框或补充框),我无法将其关闭。

这就是为什么我考虑在每一行中添加一个切换按钮来禁用过滤器。通过这样做,我将能够考虑或不考虑 checked 属性。

这是我想要的一个例子:

下面是一个可复制的例子

from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(550, 350)
        MainWindow.setMinimumSize(QtCore.QSize(550, 350))
        MainWindow.setMaximumSize(QtCore.QSize(550, 350))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(20, 20, 500, 200))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        self.pushButton_Save = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_Save.setGeometry(QtCore.QRect(120, 250, 100, 50))
        self.pushButton_Save.setMinimumSize(QtCore.QSize(100, 50))
        self.pushButton_Save.setMaximumSize(QtCore.QSize(100, 50))
        self.pushButton_Save.setObjectName("pushButton_Save")

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 550, 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"))
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "Name"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "Filter"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "complement"))
        self.pushButton_Save.setText(_translate("MainWindow", "Save"))
        self.pushButton_Save.clicked.connect(self.bindSave)

    def bindSave(self):
        numRows = self.tableWidget.rowCount()
        self.tableWidget.insertRow(numRows)

        groupButton = QtWidgets.QButtonGroup(self.tableWidget)
        groupButton.setExclusive(True)
        it1 = QtWidgets.QTableWidgetItem("filter "+str(numRows))
        self.tableWidget.setItem(numRows, 0, it1)
        ch_bx1 = QtWidgets.QCheckBox()
        groupButton.addButton(ch_bx1)
        self.tableWidget.setCellWidget(numRows, 1, ch_bx1)
        ch_bx2 = QtWidgets.QCheckBox()
        groupButton.addButton(ch_bx2)
        self.tableWidget.setCellWidget(numRows, 2, ch_bx2)



if __name__ == "__main__":
    if not QtWidgets.QApplication.instance():
        app = QtWidgets.QApplication(sys.argv)
    else:
        app = QtWidgets.QApplication.instance()
    MainWindow = QtWidgets.QMainWindow()
    ui        = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()

【问题讨论】:

唯一的问题是取消选择一组复选框吗?如果是这样,您可以更改该事件,以便如果选中则您以编程方式取消选择它。 您可以提供minimal reproducible example,也许我的 MWE 与您的不匹配,所以这可能是混乱。 好吧,听起来你的复选框就像单选按钮一样工作,一旦选择了一个,它们中的一个总是保持选中状态。要取消选择它们,您需要通过代码捕获它们并将它们关闭 - 或者 - 您可以使用复选框创建第三列并将其分配为“无过滤器”状态 @DennisJensen 完全正确!你明白我的意思。不过,我想知道是否可以通过添加开关按钮来做到这一点。 好吧,K.I.S.S.解决方案是简单地添加第三个复选框,因为这只会稍微扩展当前功能,而不是添加一个全新的功能来打开和关闭复选框的使用。还要记住,必须为每一行创建这个新功能(正如你的图片所暗示的那样),所以无论如何你本质上都会添加那个额外的列。只是思考的食物 【参考方案1】:

像其他人建议的那样,您可以通过在表格中添加第三列并将此列中的每个项目小部件设置为您对 ch_box1ch_box2 所做的那样来启用/禁用过滤器。您可以使用插槽和信号来操作检查按钮(例如,根据开关的状态启用/禁用它们)。

要像 OP 中的图片那样创建自定义开关,您可以继承 QPushButton 并覆盖 paintEvent,例如(请注意,我省略了与原始帖子中可重现示例中的代码相同的代码)

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QRect
import sys

class MySwitch(QtWidgets.QPushButton):
    def __init__(self, parent = None):
        super().__init__(parent)
        print('init')
        self.setCheckable(True)
        self.setMinimumWidth(66)
        self.setMinimumHeight(22)

    def paintEvent(self, event):
        label = "ON" if self.isChecked() else "OFF"
        bg_color = Qt.green if self.isChecked() else Qt.red

        radius = 10
        width = 32
        center = self.rect().center()

        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.translate(center)
        painter.setBrush(QtGui.QColor(0,0,0))

        pen = QtGui.QPen(Qt.black)
        pen.setWidth(2)
        painter.setPen(pen)

        painter.drawRoundedRect(QRect(-width, -radius, 2*width, 2*radius), radius, radius)
        painter.setBrush(QtGui.QBrush(bg_color))
        sw_rect = QRect(-radius, -radius, width + radius, 2*radius)
        if not self.isChecked():
            sw_rect.moveLeft(-width)
        painter.drawRoundedRect(sw_rect, radius, radius)
        painter.drawText(sw_rect, Qt.AlignCenter, label)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        ....
        self.tableWidget.setColumnCount(4)
        ....
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(3, item)
        ...

    def retranslateUi(self, MainWindow):
        ...
        item = self.tableWidget.horizontalHeaderItem(3)
        item.setText(_translate("MainWindow", "status"))

    def bindSave(self):
        ...
        ch_bx3 = MySwitch()
        ch_bx3.setChecked(True)
        ch_bx3.clicked.connect(ch_bx1.setEnabled)
        ch_bx3.clicked.connect(ch_bx2.setEnabled)
        self.tableWidget.setCellWidget(numRows, 3, ch_bx3)


if __name__ == "__main__":
    if not QtWidgets.QApplication.instance():
        app = QtWidgets.QApplication(sys.argv)
    else:
        app = QtWidgets.QApplication.instance()
    MainWindow = QtWidgets.QMainWindow()
    ui        = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    app.exec()

截图:

【讨论】:

非常感谢。这正是我想要的。

以上是关于pyqt中的切换按钮的主要内容,如果未能解决你的问题,请参考以下文章

在 PyQt5 中按下时如何切换按钮文本

PyQt4开关按钮ToggleButton

PyQT 按钮在 QGridLayout 中的大小不正确

PyQt5中的按钮连接有啥问题?

在对话框之间切换(QDialog)PyQt5

如何将pyqt5中的后退按钮和前进按钮添加到我的QGridLayout