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()
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:
Related
How can I emit a signal (or for now print the text) whenever the text from any QComboBox changes? A new QComboBox is created in both columns every time a row is added to a QTableWidget. Eventually, that signal will trigger a redraw of a plot.
Right now, I'm able to print the text from a QComboBox that already exists with a specific instance name, but I don't know how to do it when combo boxes are created dynamically in the table.
Main code:
import sys
from PyQt5.QtWidgets import (
QMainWindow,
QApplication,
QComboBox,
)
from cb_in_tables import Ui_MainWindow
class MyWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.comboBox.addItems(['', '1', '2', '3'])
self.ui.pushButton.clicked.connect(self.addRow)
self.ui.comboBox.currentTextChanged.connect(self.printComboBoxText)
def printComboBoxText(self, text):
print(text)
def addRow(self):
"""Add new row to data table and populate with widgets."""
# Insert row
rowPos = self.ui.tableWidget.rowCount()
self.ui.tableWidget.insertRow(rowPos)
x_combobox = QComboBox()
x_combobox.addItems(['', 'A', 'B', 'C'])
self.ui.tableWidget.setCellWidget(rowPos, 0, x_combobox)
y_combobox = QComboBox()
y_combobox.addItems(['', 'X', 'Y', 'Z'])
self.ui.tableWidget.setCellWidget(rowPos, 1, y_combobox)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MyWindow()
main.show()
sys.exit(app.exec_())
UI file:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'cb_in_tables.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(291, 299)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
self.tableWidget.setGeometry(QtCore.QRect(30, 60, 231, 161))
self.tableWidget.setObjectName("tableWidget")
self.tableWidget.setColumnCount(2)
self.tableWidget.setRowCount(0)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(1, item)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(30, 20, 113, 32))
self.pushButton.setObjectName("pushButton")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setGeometry(QtCore.QRect(160, 20, 104, 31))
self.comboBox.setObjectName("comboBox")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 291, 24))
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", "X"))
item = self.tableWidget.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "Y"))
self.pushButton.setText(_translate("MainWindow", "Add Row"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Nothing changes:
x_combobox.currentTextChanged.connect(self.printComboBoxText)
y_combobox.currentTextChanged.connect(self.printComboBoxText)
I'm setting a filtering tool in Python 3.7 and I'm using pyqt5. I've created a qtablewidget to store the filters and their complementaries chosen by the user. I'd like to allow the user to combine filters over a data so I added grouped checkboxes in each row to select wanted filters.
Which commands should I use to loop over my qtable to get which checkbox is selected please?
def bindApply(self):
checked_list = []
for i in range(self.tableWidget.rowCount()):
#print(self.tableWidget.rowCount())
if self.tableWidget.item(i, 1).checkState() == QtCore.Qt.Checked:
checked_list.append([i,1])
elif self.tableWidget.item(i, 2).checkState() == QtCore.Qt.Checked:
checked_list.append([i,2])
else:
pass
return(checked_list)
I expect a list containing the indexes of selected rows and columns but my function returns nothing.
Here is a reproducible 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")
self.pushButton_Apply = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_Apply.setGeometry(QtCore.QRect(260, 250, 100, 50))
self.pushButton_Apply.setMinimumSize(QtCore.QSize(100, 50))
self.pushButton_Apply.setMaximumSize(QtCore.QSize(100, 50))
self.pushButton_Apply.setObjectName("pushButton_Apply")
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", "complementary"))
self.pushButton_Save.setText(_translate("MainWindow", "Save"))
self.pushButton_Apply.setText(_translate("MainWindow", "Apply"))
#============================ BINDING ===============================
self.pushButton_Save.clicked.connect(self.bindSave)
self.pushButton_Apply.clicked.connect(self.bindApply)
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)
def bindApply(self):
checked_list = []
for i in range(self.tableWidget.rowCount()):
#print(self.tableWidget.rowCount())
if self.tableWidget.item(i, 1).checkState() == QtCore.Qt.Checked:
checked_list.append([i,1])
elif self.tableWidget.item(i, 2).checkState() == QtCore.Qt.Checked:
checked_list.append([i,2])
else:
pass
print(checked_list)
if __name__ == "__main__":
import sys
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()
You should check the state of the check boxes assigned to the cells, not the state of the item associated with the cell, i.e.
def bindApply(self):
checked_list = []
for i in range(self.tableWidget.rowCount()):
#print(self.tableWidget.rowCount())
if self.tableWidget.cellWidget(i, 1).isChecked():
checked_list.append([i,1])
elif self.tableWidget.cellWidget(i, 2).isChecked():
checked_list.append([i,2])
else:
pass
print(checked_list)
I have three groupBoxes with two radioButtons each. Buttons in the first box
enable/disable the second groupBox. Buttons in the second enable/disable the
third groupBox.
This is how it's supposed to work.
The second and third groupBoxes are disabled by default.
radioButton in the first box sends signal toggled(bool) to enable the second
box. There, radioButton that deactivates the third box is clicked by default, a second radioButton sends signal toggled(bool) to the third box enabling it.
What actually happens is that when I enable the second box, the third box
becomes enabled. When I toggle buttons in the second box I can enable/disable the third box, but when I again disable the second box from the first and then enable it again, the third box is enabled regardles of which button is clicked in the second box.
What gives?
Fair game, here's an example (tried to make it as short as I could with Designer)(imports, first class and 'if name == ...' at the end of the file contain additional 4 spaces so that code shows as code, delete them to run):
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox.setObjectName("groupBox")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.groupBox)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.radioButton_2 = QtWidgets.QRadioButton(self.groupBox)
self.radioButton_2.setChecked(True)
self.radioButton_2.setObjectName("radioButton_2")
self.horizontalLayout_2.addWidget(self.radioButton_2)
self.radioButton = QtWidgets.QRadioButton(self.groupBox)
self.radioButton.setObjectName("radioButton")
self.horizontalLayout_2.addWidget(self.radioButton)
self.horizontalLayout_3.addLayout(self.horizontalLayout_2)
self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1)
self.groupBox_2 = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox_2.setEnabled(False)
self.groupBox_2.setObjectName("groupBox_2")
self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.groupBox_2)
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.radioButton_3 = QtWidgets.QRadioButton(self.groupBox_2)
self.radioButton_3.setChecked(True)
self.radioButton_3.setObjectName("radioButton_3")
self.horizontalLayout_4.addWidget(self.radioButton_3)
self.radioButton_4 = QtWidgets.QRadioButton(self.groupBox_2)
self.radioButton_4.setObjectName("radioButton_4")
self.horizontalLayout_4.addWidget(self.radioButton_4)
self.horizontalLayout_5.addLayout(self.horizontalLayout_4)
self.gridLayout.addWidget(self.groupBox_2, 1, 0, 1, 1)
self.groupBox_3 = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox_3.setEnabled(False)
self.groupBox_3.setObjectName("groupBox_3")
self.gridLayout.addWidget(self.groupBox_3, 2, 0, 1, 1)
self.horizontalLayout.addLayout(self.gridLayout)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 30))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.radioButton.toggled['bool'].connect(self.groupBox_2.setEnabled)
self.radioButton_4.toggled['bool'].connect(self.groupBox_3.setEnabled)
self.radioButton_2.toggled['bool'].connect(self.groupBox_3.setDisabled)
self.radioButton_3.toggled['bool'].connect(self.groupBox_3.setDisabled)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", "whatever"))
self.radioButton_2.setText(_translate("MainWindow", "Off"))
self.radioButton.setText(_translate("MainWindow", "Sensors"))
self.groupBox_2.setTitle(_translate("MainWindow", "Control method"))
self.radioButton_3.setText(_translate("MainWindow", "On/Off"))
self.radioButton_4.setText(_translate("MainWindow", "PID"))
self.groupBox_3.setTitle(_translate("MainWindow", "PID settings"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
First off (and I mean this in the nicest way) that is some really ugly code. I have taken your code and cleaned it up and using Python 3.7 with pyqt5 on Win10 it works fine and should be a lot easier to understand. I hope this helps you down a more structured path to development. Keep in mind that you often have to go back and revisit and/or reuse old code that you have written best to write it in a way that makes it really easy to understand at a glance.
from sys import exit as sysExit
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class BoxOne(QGroupBox):
def __init__(self, CentrPane):
QGroupBox.__init__(self, CentrPane)
# Reference back to parent
self.CntrPane = CentrPane
self.setTitle('Main Controller')
self.radBtnOff = QRadioButton('Off')
self.radBtnOff.setChecked(True)
self.radBtnOff.toggled['bool'].connect(self.BoxTwoOff)
self.radBtnSnsr = QRadioButton('Sensors')
self.radBtnSnsr.toggled['bool'].connect(self.BoxTwoOn)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.addWidget(self.radBtnOff)
self.horizontalLayout.addWidget(self.radBtnSnsr)
self.setLayout(self.horizontalLayout)
def BoxTwoOff(self):
# I assume you did not mean to leave Box Three Enabled
self.CntrPane.SecndBox.radBtnOnOff.setChecked(True)
self.CntrPane.SecndBox.setDisabled(True)
def BoxTwoOn(self):
self.CntrPane.SecndBox.setEnabled(True)
class BoxTwo(QGroupBox):
def __init__(self, CentrPane):
QGroupBox.__init__(self, CentrPane)
# Reference back to parent
self.CntrPane = CentrPane
self.setTitle('Control Method')
self.setEnabled(False)
self.radBtnOnOff = QRadioButton('On/Off')
self.radBtnOnOff.setChecked(True)
self.radBtnOnOff.toggled['bool'].connect(self.BoxThreeOff)
self.radBtnPID = QRadioButton('PID')
self.radBtnPID.toggled['bool'].connect(self.BoxThreeOn)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.addWidget(self.radBtnOnOff)
self.horizontalLayout.addWidget(self.radBtnPID)
self.setLayout(self.horizontalLayout)
def BoxThreeOff(self):
self.CntrPane.ThirdBox.setDisabled(True)
def BoxThreeOn(self):
self.CntrPane.ThirdBox.setEnabled(True)
class BoxThree(QGroupBox):
def __init__(self, CentrPane):
QGroupBox.__init__(self, CentrPane)
# Reference back to parent
self.CntrPane = CentrPane
self.setTitle('PID Settings')
self.setEnabled(False)
self.MyEditor = QTextEdit('Editorial')
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.addWidget(self.MyEditor)
self.setLayout(self.horizontalLayout)
class CenterPanel(QWidget):
def __init__(self, MainWin):
QWidget.__init__(self)
# Reference back to parent
self.MainWin = MainWin
self.FirstBox = BoxOne(self)
self.SecndBox = BoxTwo(self)
self.ThirdBox = BoxThree(self)
self.gridLayout = QGridLayout()
self.gridLayout.addWidget(self.FirstBox, 0, 0, 1, 1)
self.gridLayout.addWidget(self.SecndBox, 1, 0, 1, 1)
self.gridLayout.addWidget(self.ThirdBox, 2, 0, 1, 1)
self.setLayout(self.gridLayout)
class MenuToolBar(QDockWidget):
def __init__(self, MainWin):
QDockWidget.__init__(self)
# Reference back to parent
self.MainWin = MainWin
self.MainMenu = MainWin.menuBar()
class StatusBar(QDockWidget):
def __init__(self, MainWin):
QDockWidget.__init__(self)
# Reference back to parent
self.MainWin = MainWin
self.MainMenu = MainWin.statusBar()
class UI_MainWindow(QMainWindow):
def __init__(self, AppDesktop):
super(UI_MainWindow, self).__init__(AppDesktop)
# Reference back to parent
self.MainDeskTop = AppDesktop
self.setWindowTitle('Main Window')
# Left, Top, Width, Height
self.setGeometry(200, 200, 800, 600)
self.CenterPane = CenterPanel(self)
self.setCentralWidget(self.CenterPane)
self.MenuToolBar = MenuToolBar(self)
self.StatusBar = StatusBar(self)
if __name__ == '__main__':
MainApp = QApplication([])
MainGui = UI_MainWindow(MainApp.desktop())
MainGui.show()
sysExit(MainApp.exec_())
I have a class called TwoStateButton which inherits QPushButton so that the icon can change when the button is being clicked on and so the icon gets resized in proportion to the button when the whole window gets resized.
When I run this code, the button takes up the whole window and the icon is about 10% of the button. I'm trying to get it so that the icon image takes up the entire pushbutton and that they both get resized by the same amount when the window is resized.
What modifications to my code should I do to achieve this?
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class TwoStateButton(QtWidgets.QPushButton):
def __init__(self, on, hover, parent=None):
super(TwoStateButton, self).__init__(parent)
self.pad = 4 # padding between the icon and the button frame
self.minSize = 8 # minimum size of the icon
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
)
self.setSizePolicy(sizePolicy)
self.setIcon(QtGui.QIcon(on))
self.on = on
self.hover = hover
def mousePressEvent(self, event):
super(TwoStateButton, self).mousePressEvent(event)
self.setIcon(QtGui.QIcon(self.hover))
def mouseReleaseEvent(self, event):
super(TwoStateButton, self).mouseReleaseEvent(event)
self.setIcon(QtGui.QIcon("%s" % (self.on)))
def paintEvent(self, event):
qp = QtGui.QPainter(self)
opt = QtWidgets.QStyleOptionButton()
self.initStyleOption(opt)
r = opt.rect
h = r.height()
w = r.width()
iconSize = max(min(h, w) - 2 * self.pad, self.minSize)
opt.iconSize = QtCore.QSize(iconSize, iconSize)
self.style().drawControl(QtWidgets.QStyle.CE_PushButton, opt, qp,self)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1090, 681)
MainWindow.setStyleSheet("")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.pushButton_2 = TwoStateButton(
"images/Button5-3.png", "images/Button5-4.png", self.centralwidget
)
self.pushButton_2.setGeometry(QtCore.QRect(430, 310, 141, 81))
self.pushButton_2.setText("")
self.pushButton_2.setCheckable(False)
self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout.addWidget(self.pushButton_2)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1090, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setStyleSheet(
"border-image: url(:/images/Button5-3.png)"
)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I have a PyQt5 QTableWidget for which I want to detect double-right-click events.
Here is my 'design.py' module:
from PyQt5 import QtCore, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(790, 472)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.tbwMain = QtWidgets.QTabWidget(self.centralwidget)
self.tbwMain.setGeometry(QtCore.QRect(0, 0, 801, 451))
self.tbwMain.setObjectName("tbwMain")
self.tabBoxes = QtWidgets.QWidget()
self.tabBoxes.setObjectName("tabBoxes")
self.horizontalLayoutWidget = QtWidgets.QWidget(self.tabBoxes)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 791, 421))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem = QtWidgets.QSpacerItem(220, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.horizontalLayout.addItem(spacerItem)
self.tblBoxes = QtWidgets.QTableWidget(self.horizontalLayoutWidget)
self.tblBoxes.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.tblBoxes.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.tblBoxes.setRowCount(1)
self.tblBoxes.setObjectName("tblBoxes")
self.tblBoxes.setColumnCount(3)
item = QtWidgets.QTableWidgetItem()
self.tblBoxes.setHorizontalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
self.tblBoxes.setHorizontalHeaderItem(1, item)
item = QtWidgets.QTableWidgetItem()
self.tblBoxes.setHorizontalHeaderItem(2, item)
item = QtWidgets.QTableWidgetItem()
item.setTextAlignment(QtCore.Qt.AlignCenter)
self.tblBoxes.setItem(0, 0, item)
item = QtWidgets.QTableWidgetItem()
item.setTextAlignment(QtCore.Qt.AlignCenter)
self.tblBoxes.setItem(0, 1, item)
item = QtWidgets.QTableWidgetItem()
item.setTextAlignment(QtCore.Qt.AlignCenter)
self.tblBoxes.setItem(0, 2, item)
self.tblBoxes.horizontalHeader().setStretchLastSection(True)
self.tblBoxes.verticalHeader().setVisible(False)
self.horizontalLayout.addWidget(self.tblBoxes)
spacerItem1 = QtWidgets.QSpacerItem(220, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.horizontalLayout.addItem(spacerItem1)
self.tbwMain.addTab(self.tabBoxes, "")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.tbwMain.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
# - - - - -
self.tblBoxes.doubleClicked.connect(self.tblMouseDoubleClick)
def tblMouseDoubleClick(self):
pass #Get event somehow?
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
As well as my 'main.py' module:
from PyQt5 import QtCore, QtGui, QtWidgets
from design import Ui_MainWindow
import shared.components.tables as tbl
from unshared import boxes as bxs
import switch as swb
stop_threads = False
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
QtWidgets.QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
load(self)
def mouseDoubleClickEvent(self, QMouseEvent):
if event.type() == QtCore.QEvent.MouseButtonPress:
if event.button() == QtCore.Qt.RightButton:
print('Double right-click!')
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I've tried setting up the event by using mouseDoubleClickEvent in the main.py module; however, the clicks only register when clicking in areas of the main window outside of the table.
I've also considered using connect (as shown in design.py), but I don't know how to check to see if the right mouse button is being clicked without the mouse event - and I'm not sure if it is possible for me to get the mouse event. I just want to make it so that when the user double-right-clicks on a cell in my table (tblBoxes) I can detect the event.
Is this possible with either of the methods I've proposed? I'm using Qt Designer, so the less invasive the solution the better.
You can use an event-filter to watch for a MouseButtonDblClick event:
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
...
self.tblBoxes.viewport().installEventFilter(self)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.MouseButtonDblClick and
event.buttons() == QtCore.Qt.RightButton and
source is self.tblBoxes.viewport()):
item = self.tblBoxes.itemAt(event.pos())
if item is not None:
print('dblclick:', item.row(), item.column())
return super(MainWindow, self).eventFilter(source, event)