Find out whether PyQt QPushButton is checked using self.sender() - python

The QPushButton lightsBtn is a toggle button that switches a light on and off. When the user presses lightsBtn, the function lightsBtnHandler will check whether the button is currently checked or not and calls either turnOnLights or turnOffLights.
I think self.sender() is able to access properties of the QPushButton, but I cant find any documentation on accessing the checked state.
Is it possible?
class Screen(QMainWindow):
def initUI(self):
lightsBtn= QPushButton('Turn On')
lightsBtn.setCheckable(True)
lightsBtn.setStyleSheet("QPushButton:checked {color: white; background-color: green;}")
lightsBtn.clicked.connect(self.lightsBtnHandler)
lightsBtn.show()
def lightsBtnHandler(self):
if self.sender().?? isChecked(): # How to check for checked state?
self.turnOnLights()
else:
self.turnOffLights()

following the #Matho comment I have modified your code a little bit.
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
import sys
class Screen(QMainWindow):
def __init__(self):
super(Screen, self).__init__()
self.initUI()
def initUI(self):
self.lightsBtn= QPushButton('Turn On')
self.lightsBtn.setCheckable(True)
self.lightsBtn.setStyleSheet("QPushButton:checked {color: white; background-color: green;}")
self.lightsBtn.clicked.connect(self.lightsBtnHandler)
# probaply you will want to set self.lightsBtn
# at certain spot using layouts
self.setCentralWidget(self.lightsBtn)
def lightsBtnHandler(self):
if self.lightsBtn.isChecked():
self.turnOnLights()
else:
self.turnOffLights()
def turnOnLights(self):
print("truned on")
def turnOffLights(self):
print("truned off")
app = QApplication(sys.argv)
window = Screen()
window.show()
sys.exit(app.exec_())

Related

How can I make my QLabel scroll with the written text

Im making a calculator with pyqt5 but when the calculator QLabel overflow I need it to scroll with the last digit on the QLabel
One option is to use a read-only QLineEdit instead of a QLabel. For example
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import Qt
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.label = QtWidgets.QLineEdit()
# mimic QLabel by making self.label read-only and removing the frame and background color
self.label.setReadOnly(True)
self.label.setStyleSheet("background-color:#00000000; font-size: 20px; border:0px")
self.label.setAlignment(Qt.AlignCenter)
self.text_edit = QtWidgets.QLineEdit(self)
self.text_edit.setPlaceholderText('type something')
self.vlayout = QtWidgets.QVBoxLayout(self)
self.vlayout.addWidget(self.label)
self.vlayout.addWidget(self.text_edit)
self.text_edit.textChanged.connect(self.label.setText)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = Window()
window.show()
app.exec()
Screenshots:

style for a class instance using Qt Style Sheets

I am trying to write a CSS style sheet for toy PyQt5 application. I have successfully defined a classes MainWindow(QMainWindow) and DefaultWidget(QWidget), with the latter containing two push buttons, which I am trying to stylize.
Using the C++ docs, I've been trying to take advantage of the QPushButton#evilButton example, provided in: Link
However, it appears that the # notation for an instance name does not translate to the analogous self.setStyleSheet() method for an external CSS file in my python application.
Python file (apologies if there are typos - transcribing from another computer):
import sys
from PyQt5.Widgets import QApplication, QMainWindow, QWidget, QPushButton, QHBoxLayout
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setStyleSheet(open('./style.css').read())
try:
self.setCentralWidget(DefaultWidget())
else:
some_error_stuff_for_troubleshooting
self.show()
class DefaultWidget(QWidget):
def __init__(self):
super().__init__()
okButton = QPushButton('OK')
cancelButton = QPushButton('Cancel')
hbox = QHBoxLayout()
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)
self.setLayout(hbox)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
In the CSS file...
QWidget.MainWindow {
background: blue
}
QPushButton {
background-color: beige
}
QPushButton???okButton {
background-color: green
}
Where I put the ??? I have tried a number of things (trying to walk through the classes QPushButton.okButton, QPushButton.DefaultWidget.okButton, DefaultWidget.okButton, with ., #, :: and : notation, etc.) but at this point it's more random than educated guessing.
I would like for the CSS to handle some naming convention styling, outside of the class name to help pull as much style out of the python file as possible. Even if the notation happened to be correct, am I not seeing my expected color change due to inherited style?
Edit: If anyone stumbles across this, the .setObjectName() method or declaring the object name option when instancing the widget. Also, here's a similar question on why some sort of default name isn't used, if you're interested.
PyQt: is there an better way to set objectName in code?
Try it:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QHBoxLayout
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# open('./style.css').read())
self.setStyleSheet("""
QWidget {
background: blue;
}
QPushButton {
background-color: beige;
}
QPushButton#okButton { /* <--- #okButton */
background-color: green;
}
""")
defaultWidget = DefaultWidget()
self.setCentralWidget(defaultWidget)
self.show()
class DefaultWidget(QWidget):
def __init__(self):
super().__init__()
okButton = QPushButton('OK', objectName="okButton") # + objectName="okButton"
cancelButton = QPushButton('Cancel')
hbox = QHBoxLayout()
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)
self.setLayout(hbox)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())

PyQt5 GUI can't be close from Spyder

I am trying to create a simple GUI using PyQt5. I am running my code in windows 10 from Spyder(Anaconda latest version, python 3.7). Here is my code:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 button test'
self.left = 50
self.top = 50
self.width = 720
self.height = 800
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
button = QPushButton('PyQt5 button', self)
button.setToolTip('This is an example button')
button.move(100,70)
button.clicked.connect(self.on_click)
self.show()
#pyqtSlot()
def on_click(self):
print('PyQt5 button click')
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
app.exec_()
A window pop up. Now if I close the GUI by clicking on closing button(top right corner of the GUI),Spyder IP console does not return to normal condition. It freezes. What should I use in code to solve the issue?
Thanks,
Moni
Closing the window doesn't appear to close the QApplication object. To do this, override the closeEvent function of the main window by adding these two lines after the on_Click definition
def closeEvent(self,event):
QApplication.quit()
This will close the window and the QApplication object as well and control should return to the console
You can try and look at the answer in the following thread:
Can't Kill PyQT Window after closing it. Which requires me to restart the kernal
Seems to have resemblance to your problem and a solution.

PyQt5: Validate LineEdit through QPushButton using signals and slots

Having trouble connecting a QPushButton to a QMessageBox in PyQt5 as there seems to be little documentation in comparison with PyQt4. As it stands the QmessageBox is executing before the main layout I believe this to be an issue somewhere with .self and .exec_()?
The second, and main issue however is concerning connecting the two widgets. I am looking to implement some form of validity check; i.e when both QLineEdit fields contain text then on the click of 'Submit' the form should clear the fields, however if either of the fields are left blank when the 'submit' is clicked, Id like the QMessageBox to be opened. I'm unsure how to implement this as I don't know how to connect both the textField AND the PushButton together.
from PyQt5.QtWidgets import QWidget, QLabel, QLineEdit,QSpinBox,
QDoubleSpinBox, QComboBox, QRadioButton, QPushButton, QHBoxLayout, QVBoxLayout,
QTextEdit, QGridLayout, QApplication, QMessageBox
from PyQt5.QtCore import Qt, pyqtSlot
import csv
class Buttons (QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.Widgets()
self.arrange()
self.close()
self.messageBox()
self.setWindowTitle ("test")
def Widgets(self):
self.nameLabel = QLabel("Name: ")
self.nameLineEdit = QLineEdit()
self.surnameLabel = QLabel("Surname: ")
self.surnameLineEdit = QLineEdit()
self.button1 = QPushButton("submit")
self.button2 = QPushButton("cancel")
self.button1.setMaximumSize(150,20)
self.button2.setMaximumSize(150,20)
def arrange (self):
nameField = QVBoxLayout()
nameField.addWidget(self.nameLabel)
nameField.addWidget(self.nameLineEdit)
nameField.addWidget(self.surnameLabel)
nameField.addWidget(self.surnameLineEdit)
#QHBoxLayout for Buttons:
buttonsLayout = QHBoxLayout()
buttonsLayout.addWidget(self.button1)
buttonsLayout.addWidget(self.button2)
self.button1.setSizePolicy(10,10)
self.button2.setSizePolicy(10,10)
#Creating mainLayout:
mainLayout = QVBoxLayout()
mainLayout.addLayout(nameField)
mainLayout.addLayout(buttonsLayout)
self.setLayout(mainLayout)
#pyqtSlot()
def close(self):
#close window
self.button2.clicked.connect(app.quit)
def clear(self):
pass
#Clear textFields when button is clicked:
#pyqtSlot()
def messageBox(self):
self.message = QMessageBox()
self.message.setText ("submit ERROR")
self.message.setStandardButtons(QMessageBox.Ok)
self.button1.clicked.connect(self.messageBox)
self.message.exec_()
I have tried a few different techniques none of which have worked successfully
self.connect(self.Button1, SIGNAL("clicked()"),self.clicked)
def clicked(self):
QMessageBox.about(self, "message!"(self.messageBox()))
if __name__ == "__main__":
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication (sys.argv)
window = Buttons()
window.show()
sys.exit (app.exec_())
Read over your program and follow the path of execution. Start with window = Buttons() which means the Buttons.__init__ method runs. Within this you run Buttons.messageBox which connects the messageBox slot to the clicked signal of button1. It then opens the message box (self.message.exec_()). Your __init__ method eventually finishes and then app.exec_() is called which actually shows the main GUI and starts the Qt event loop which processes things like button clicks and redraw events.
At this point, your program has already been told to show the message box and is configured to run Buttons.messageBox when you click the button. When you click the button, it shows the message box, and makes an additional connection for the button. Next time you click the button you will get 2 message boxes showing!
The solution is to separate out the signal connection from the messageBox slot. Rather than calling self.messageBox() in your __init__ method, make the signal connection for button1 there.
class Buttons (QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.Widgets()
self.arrange()
self.close()
self.button1.clicked.connect(self.messageBox)
self.setWindowTitle ("test")
#pyqtSlot()
def messageBox(self):
self.message = QMessageBox()
self.message.setText ("submit ERROR")
self.message.setStandardButtons(QMessageBox.Ok)
self.message.exec_()

(Py)Qt: Spacing in QHBoxLayout shows centralwidget's background, not parent's

Consider the following example code:
from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QLabel, QWidget,
QMainWindow, QVBoxLayout, QTextEdit)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
cwidget = QWidget(self)
cwidget.setStyleSheet("QWidget { background-color: red; }")
self.setCentralWidget(cwidget)
self.resize(100, 100)
vbox = QVBoxLayout(cwidget)
vbox.addWidget(QTextEdit(self))
vbox.addWidget(BlackBar(self))
class BlackBar(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setStyleSheet("* { background-color: black; color: white; }")
hbox = QHBoxLayout(self)
hbox.setSpacing(5)
hbox.addWidget(QLabel(text="eggs"))
hbox.addWidget(QLabel(text="bacon"))
if __name__ == '__main__':
app = QApplication([])
main = MainWindow()
main.show()
app.exec_()
It has:
A QMainWindow, QWidget as central widget (red), QVBoxLayout as a child of the cental widget. Inside there:
A QTextEdit (just as a filler)
A QWidget (black), which contains a QHBoxLayout. Inside that:
Two QLabels
This looks like this:
I'd expect the spaces between the labels to be black, because the QHBoxLayout is a child of BlackBar, but it seems BlackBar is just "invisible" in between and the central widget "shines through". Why is this?
The bugreport has now been answered with a solution that's easier than #ekhumoro's answer and works:
I don't think this is valid. The paint code your are looking for is not drawn in the paintEvent. Look for QWidgetPrivate::paintBackground instead. For performance reasons widgets will ignore style sheets by default, but you can set the WA_StyledBackground attribute on the widget and it should respect style sheet backgrounds.
And indeed, doing this before setting the stylesheet does the trick:
self.setAttribute(Qt.WA_StyledBackground)
Although the Style Sheet Syntax does not mention it, it seems that the QWidget class is treated differently when it comes to stylesheets.
Other widgets will work fine with your example code. For example, if QWidget is replaced everywhere with QFrame, then everything works as expected.
To get stylesheet support for QWidget subclasses, you need to reimplement the paintEvent and enable it explicitly:
class BlackBar(QWidget):
...
def paintEvent(self, event):
option = QStyleOption()
option.initFrom(self)
painter = QPainter(self)
self.style().drawPrimitive(
QStyle.PE_Widget, option, painter, self)

Categories

Resources