Reputation: 139
Is it possible to create a switch button with pyqt5?
I'm designing a filtering tool in python using pyqt5. The user is supposed to be able to apply a filter or its complement on some data and even combine filters.
I display possible filters in qtablewidget where the user can choose filters to apply using checkboxes. In each row, checkboxes are exclusives i.e. the user can not select a filter and its complement at the same time.
But the problem is that once we select a checkbox in a row, we cannot deselect it unless we select its opposite. In fact, when the filters are just loaded , all the boxes are unchecked (they are empty in a way) so I can choose which filter to apply but when I want to choose an other filter, one of the boxes of the precedent filter still checked (either box Filter or box Complement is checked) and I can not switch it off.
That's why I thought about adding a switch button in each row to disable a filter. By doing this I will be able to take in consideration or not the checked attribute.
Here is an example of what I want :
Below an reproductible example
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()
Upvotes: 6
Views: 15866
Reputation: 24430
Like others have suggested, you could enable/disable filters by adding a third column to the table and setting each item widget in this column to a like you did for ch_box1
and ch_box2
. You can use slots and signals to manipulate the check buttons (for example enable/disable them depending on the state of the switch).
To create custom switches like in the picture in the OP, you could sub-class QPushButton
and overriding paintEvent
, e.g. (note that I've omitted the code that was the same as the code in the reproducable example in the original post)
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()
Screenshot:
Upvotes: 16