How to iterate all QPushButton in a QGroupBox - python

I have a QDialog which contains a QGroupBox which in turn contains some push-buttons. I want to differentiate the background-color of the push-button which is clicked and all the remaining push-buttonx. How to achieve this?

Make sure the buttons are all children of the group-box, and then use findChildren to iterate over them. You could also use a QButtonGroup to help manage the buttons.
Here's a demo script to show how it could be done:
from PyQt5 import QtCore, QtWidgets
class Dialog(QtWidgets.QDialog):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.groupBox = QtWidgets.QGroupBox(self)
layout.addWidget(self.groupBox)
layout = QtWidgets.QVBoxLayout(self.groupBox)
for index in range(5):
button = QtWidgets.QPushButton('Button %d' % index, self.groupBox)
layout.addWidget(button)
self.buttonGroup = QtWidgets.QButtonGroup(self)
self.buttonGroup.buttonClicked.connect(self.handleButtonClicked)
self.updateButtonGroup()
def updateButtonGroup(self):
for button in self.groupBox.findChildren(QtWidgets.QPushButton):
if self.buttonGroup.id(button) < 0:
self.buttonGroup.addButton(button)
def handleButtonClicked(self, button):
for item in self.buttonGroup.buttons():
if button is item:
item.setStyleSheet('background-color: orange')
else:
item.setStyleSheet('')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Dialog()
window.show()
sys.exit(app.exec_())

If reason to use class QtGui.QGroupBox is select button just like radio button, You can use Qt Style Sheet to set difference background color;
import sys
from PyQt4 import QtGui
class QCustomDialog (QtGui.QDialog):
def __init__(self, *args, **kwargs):
QtGui.QDialog.__init__(self, *args, **kwargs)
myQVBoxLayout = QtGui.QVBoxLayout()
for text in ['PyQt', 'Stack', 'Overflow']:
myQPushButton = QtGui.QPushButton(text)
myQPushButton.setCheckable(True)
myQPushButton.setAutoExclusive(True)
myQVBoxLayout.addWidget(myQPushButton)
myQVBoxLayout.addStretch(1)
myQGroupBox = QtGui.QGroupBox()
myQGroupBox.setStyleSheet('''
QPushButton {
border: 0px;
color: rgb(255, 255, 255);
background-color: rgb(0, 0, 0);
}
QPushButton:checked {
border: 0px;
color: rgb(255, 255, 255);
background-color: rgb(255, 0, 0);
}
''')
myQGroupBox.setLayout(myQVBoxLayout)
allQVBoxLayout = QtGui.QVBoxLayout()
allQVBoxLayout.addWidget(myQGroupBox)
self.setLayout(allQVBoxLayout)
myQApplication = QtGui.QApplication([])
myQCustomDialog = QCustomDialog()
myQCustomDialog.show()
sys.exit(myQApplication.exec_())

Related

Display splash-screen in PyQt5 during function execution

I have a program where I display the GUI with PyQt5. From my MainWindow I open a QWidget from which a function is started. While this function is running a SplashScreen should appear. As soon as the function is finished, the SplashScreen should close again. I have tried to start the SplashScreen from within the function. However, the SplashScreen does not appear.
I have used the following code:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QDialog, QProgressBar, QLabel, QFrame, QMainWindow, QVBoxLayout, QPushButton
from PyQt5.QtCore import Qt
import time
class Function:
def loading(self):
self.screen = SplashScreen()
self.screen.show()
time.sleep(10)
self.screen.close()
class SplashScreen(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle('Spash Screen Example')
self.setFixedSize(1100, 500)
self.setWindowFlag(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setWindowFlag(Qt.WindowStaysOnTopHint)
self.initUI()
def initUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
self.frame = QFrame()
layout.addWidget(self.frame)
self.labelTitle = QLabel(self.frame)
self.labelTitle.setObjectName('LabelTitle')
# center labels
self.labelTitle.resize(self.width() - 10, 150)
self.labelTitle.move(0, 40) # x, y
self.labelTitle.setText('Splash Screen')
self.labelTitle.setAlignment(Qt.AlignCenter)
self.labelDescription = QLabel(self.frame)
self.labelDescription.resize(self.width() - 10, 50)
self.labelDescription.move(0, self.labelTitle.height())
self.labelDescription.setObjectName('LabelDesc')
self.labelDescription.setText('<strong>Working on Task #1</strong>')
self.labelDescription.setAlignment(Qt.AlignCenter)
self.progressBar = QProgressBar(self.frame)
self.progressBar.resize(self.width() - 200 - 10, 50)
self.progressBar.move(100, self.labelDescription.y() + 130)
self.progressBar.setAlignment(Qt.AlignCenter)
self.progressBar.setFormat('%p%')
self.progressBar.setTextVisible(True)
self.progressBar.setRange(0, 100)
self.progressBar.setValue(20)
self.labelLoading = QLabel(self.frame)
self.labelLoading.resize(self.width() - 10, 50)
self.labelLoading.move(0, self.progressBar.y() + 70)
self.labelLoading.setObjectName('LabelLoading')
self.labelLoading.setAlignment(Qt.AlignCenter)
self.labelLoading.setText('loading...')
self.setStyleSheet('''
#LabelTitle {
font-size: 60px;
color: #93deed;
}
#LabelDesc {
font-size: 30px;
color: #c2ced1;
}
#LabelLoading {
font-size: 30px;
color: #e8e8eb;
}
QFrame {
background-color: #2F4454;
color: rgb(220, 220, 220);
}
QProgressBar {
background-color: #DA7B93;
color: rgb(200, 200, 200);
border-style: none;
border-radius: 10px;
text-align: center;
font-size: 30px;
}
QProgressBar::chunk {
border-radius: 10px;
background-color: qlineargradient(spread:pad x1:0, x2:1, y1:0.511364, y2:0.523, stop:0 #1C3334, stop:1 #376E6F);
}
''')
class Window(QWidget):
def __init__(self):
super().__init__()
layout_window = QVBoxLayout()
self.setLayout(layout_window)
self.button = QPushButton("Open", self)
layout_window.addWidget(self.button)
self.button.clicked.connect(self.open)
def open(self):
self.function = Function()
self.function.loading()
class MyApp(QMainWindow):
def __init__(self):
super().__init__()
self.window_width, self.window_height = 1200, 800
self.setMinimumSize(self.window_width, self.window_height)
layout = QVBoxLayout()
self.setLayout(layout)
self.button = QPushButton("Open", self)
layout.addWidget(self.button)
self.button.clicked.connect(self.open)
def open(self):
self.window = Window()
self.window.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
myapp = MyApp()
myapp.show()
try:
sys.exit(app.exec_())
except SystemExit:
print('Closing Window...')
I have represented the function (in the original program some time consuming data processing takes place) with time.sleep. When I leave out the close-function, the 'SplashScreen' opens after the time.sleep is finished.
The time.sleep will block the event-loop, so any GUI-related operations will be suspended until it finishes. You therefore need to explicitly force processing of any pending events, using processEvents:
class Function:
def loading(self):
self.screen = SplashScreen()
self.screen.show()
for i in range(100):
self.screen.progressBar.setValue(i)
QApplication.processEvents()
time.sleep(0.1)
self.screen.close()
Or move the time-consuming task to a worker thread, and then use signals to update the GUI:
from PyQt5.QtCore import pyqtSignal, QThread
class Thread(QThread):
progressChanged = pyqtSignal(int)
def run(self):
for i in range(100):
QThread.msleep(100)
self.progressChanged.emit(i)
class Function:
def loading(self):
self.screen = SplashScreen()
self.screen.show()
self.thread = Thread()
self.thread.progressChanged.connect(
self.screen.progressBar.setValue)
self.thread.finished.connect(self.screen.close)
self.thread.start()
The latter approach is usually recommended, but if the task can't be broken up into discrete steps, you may need to use multiprocessing instead.

Button doesn't show up

i'm trying to do a simple GUI for a python script that convert some text into a specific format but buttons doesn't show up in the window.
I first create the button class
class Button(QPushButton):
def __init__(self, btn_name=None):
super().__init__()
self.button = QPushButton(btn_name)
self.button.setCursor(
QCursor(QtCore.Qt.CursorShape.PointingHandCursor))
self.button.setStyleSheet(
"""*{border: 4px solid 'green';
border-radius: 45px;
font-size: 35px;
color: 'white';
padding: 25px 0;
margin: 100px, 200px}
*:hover{background: 'green'}
*:pressed{background: 'lightgreen'}"""
)
Then create the window class like this
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.window = QWidget()
self.window.resize(500, 500)
self.window.setWindowTitle("Pantuflis Software")
self.window.setFixedWidth(1000)
self.window.setStyleSheet("background: 'black';")
self.grid = QGridLayout()
self.window.setLayout(self.grid)
self.button = Button("Play")
self.grid.addWidget(self.button)
self.window.show()
Finally add the rest
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec())
But the button doesn't show up, only the main window does. I also tried the same but without creataing the button from my own class and works. Must be something wrong in the button class but i can't see what is.
If you are going to implement inherence then you have to apply the changes to the class. In your case it has a class that inherits from QPushButton but where you create the custom button which is not necessary, the same with the main window. My recommendation is that the OP should review his notes about inheritance.
import sys
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QCursor
from PyQt6.QtWidgets import QApplication, QGridLayout, QPushButton, QWidget
class Button(QPushButton):
def __init__(self, btn_name=""):
super().__init__(btn_name)
self.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
self.setStyleSheet(
"""*{border: 4px solid 'green';
border-radius: 45px;
font-size: 35px;
color: 'white';
padding: 25px 0;
margin: 100px, 200px}
*:hover{background: 'green'}
*:pressed{background: 'lightgreen'}"""
)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.resize(500, 500)
self.setWindowTitle("Pantuflis Software")
self.setFixedWidth(1000)
self.setStyleSheet("background: 'black';")
self.grid = QGridLayout(self)
self.button = Button("Play")
self.grid.addWidget(self.button)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())

Setting border for checkable QListWidgetItem

I have added some check-able QListWidgetItem and I have challenge setting the border color for the checkboxes. setForeground function only sets the color of the checkbox text.
Any suggestion please.
This is my sample code creating the check-able QListWidgetItems:
watch_list = ["Protesters", "Local news staff", "Techfluencers"]
for category in watch_list:
self.checkBox = QtWidgets.QListWidgetItem(category)
self.checkBox.setFlags(self.checkBox.flags() | QtCore.Qt.ItemIsUserCheckable)
self.checkBox.setCheckState(QtCore.Qt.Unchecked)
self.checkBox.setForeground(QtGui.QColor('#FFFFFF'))
self.watchListslistWidget.addItem(self.checkBox)
I have tried
self.watchListslistWidget.setStyleSheet("""
QListWidget::item {
border:1px #FFFFFF
}
""")
But it sets the all background of the QListWidget to white.
You can use a delegate:
from PyQt5 import QtCore, QtGui, QtWidgets
class CheckBoxDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
option.palette.setBrush(QtGui.QPalette.Button, QtGui.QColor("red"))
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.watchListslistWidget = QtWidgets.QListWidget()
self.setCentralWidget(self.watchListslistWidget)
watch_list = ["Protesters", "Local news staff", "Techfluencers"]
for category in watch_list:
checkBox = QtWidgets.QListWidgetItem(category)
checkBox.setFlags(checkBox.flags() | QtCore.Qt.ItemIsUserCheckable)
checkBox.setCheckState(QtCore.Qt.Unchecked)
self.watchListslistWidget.addItem(checkBox)
delegate = CheckBoxDelegate(self.watchListslistWidget)
self.watchListslistWidget.setItemDelegate(delegate)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I got it working by using indicator as follows:
self.watchListslistWidget.setStyleSheet("""
QListWidget::indicator{
border: 1px solid white;
}
""")

QSplitter() doesn't treat a QScrollArea() and QFrame() equally

1. Problem explained
I'm experimenting with Qt's QSplitter() widget. I've built a very simple sample project in PyQt5 showing a QSplitter() encapsulating a QScrollArea() on the left and a QFrame() on the right:
I've given both the QScrollArea() and QFrame() equal stretch factors, but the QSplitter() doesn't treat them equally. The QScrollArea() always gets most space. I have no idea why.
Minimal, Reproducible Example
Simply copy-paste the code below in a .py script and run it. I've got Python 3.7 with PyQt5 running on a Windows 10 machine.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Scroller(QScrollArea):
'''
The Scroller(), will be first widget in the Splitter().
'''
def __init__(self):
super().__init__()
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self.setStyleSheet("""
QScrollArea {
background-color:#fce94f;
border-color:#c4a000;
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
}
""")
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
# 1. FRAME
self.__frm = QFrame()
self.__frm.setStyleSheet("QFrame { background: #ff25292d; border: none; }")
self.__frm.setMinimumHeight(100)
# 2. LAYOUT
self.__lyt = QVBoxLayout()
self.__frm.setLayout(self.__lyt)
# 3. SELF
self.setWidget(self.__frm)
self.setWidgetResizable(True)
return
class Frame(QFrame):
'''
The Frame(), will be second widget in the Splitter()
'''
def __init__(self):
super().__init__()
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
# 1. FRAME
self.setStyleSheet("""
QFrame {
background-color:#fcaf3e;
border-color:#ce5c00;
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
}
""")
self.__lyt = QVBoxLayout()
self.__lyt.setAlignment(Qt.AlignTop)
self.__lyt.setSpacing(0)
self.__lyt.setContentsMargins(10, 10, 10, 10)
self.setLayout(self.__lyt)
return
class Splitter(QSplitter):
'''
The Splitter().
'''
def __init__(self, widg1, widg2):
super().__init__()
self.setOrientation(Qt.Horizontal)
self.addWidget(widg1)
self.addWidget(widg2)
self.setStretchFactor(0, 5)
self.setStretchFactor(1, 5)
return
def createHandle(self):
return QSplitterHandle(self.orientation(), self)
class CustomMainWindow(QMainWindow):
'''
CustomMainWindow(), a QMainWindow() to start the whole setup.
'''
def __init__(self):
super().__init__()
self.setGeometry(100, 100, 600, 300)
self.setWindowTitle("QSPLITTER TEST")
# 1. OUTER FRAME
self.__frm = QFrame()
self.__frm.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.__frm.setStyleSheet("""
QFrame {
background-color: #eeeeec;
border-color: #2e3436;
}
""")
self.__lyt = QVBoxLayout()
self.__frm.setLayout(self.__lyt)
self.setCentralWidget(self.__frm)
self.show()
# 2. WIDGETS TO BE PUT IN SPLITTER
self.__widg1 = Scroller()
self.__widg2 = Frame()
# 3. SPLITTER
self.__splitter = Splitter(self.__widg1, self.__widg2)
self.__lyt.addWidget(self.__splitter)
return
if __name__== '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
QSplitter not only takes the stretch factor as a reference, it also takes into account the sizeHint(). If the following is added:
# ...
# 3. SPLITTER
self.__splitter = Splitter(self.__widg1, self.__widg2)
self.__lyt.addWidget(self.__splitter)
print(self.__widg1.sizeHint(), self.__widg2.sizeHint())
return
You get the following:
PyQt5.QtCore.QSize(38, 22) PyQt5.QtCore.QSize(20, 20)
Where we see that the QScrollArea has a greater width in the sizeHint() than the QFrame, and that explains why the observed behavior.
The solution is to establish the same width of sizeHint(), that is, not depend on what it contains.
class Scroller(QScrollArea):
# ...
def sizeHint(self):
s = super().sizeHint()
s.setWidth(20) # same width
return s
class Frame(QFrame):
# ...
def sizeHint(self):
s = super().sizeHint()
s.setWidth(20) # same width
return s
# ...
Output:

Why does a class inhering another class not produce the same results as 'another class'?

I am developing an application in Python using PyQt5. Here is the code in question:
class Dialog(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.layout = QtWidgets.QGridLayout()
self.main = QtWidgets.QWidget()
self.main.setLayout(self.layout)
self.setStyleSheet(QMainWindowStyle)
self.setCentralWidget(self.main)
self.show()
class AppearanceTab(QtWidgets.QWidget):
def __init__(self):
super().__init__()
class SettingsDialog(Dialog):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget(self)
self.tabs.setStyleSheet(QTabWidgetStyle)
self.layout.addWidget(self.tabs)
self.tab_appearance = AppearanceTab()
self.tab_appearance.setStyleSheet(QWidgetStyle)
self.tab_appearance_layout = QtWidgets.QGridLayout()
self.tab_appearance.setLayout(self.tab_appearance_layout)
self.tabs.addTab(self.tab_appearance, "Appearance")
self.tab_server = QtWidgets.QWidget()
self.tab_server.setStyleSheet(QWidgetStyle)
self.tab_server_layout = QtWidgets.QGridLayout()
self.tab_server.setLayout(self.tab_server_layout)
self.tabs.addTab(self.tab_server, "Server")
Why is it that when self.tab_appearance is an AppearanceTab instance (which should be a copy of QWidget) it has a different style to self.tab_server (i.e. background colour changes) when self.tab_server is an instance of QWidget?
The stylesheet just defines background-color: #333333 and color: #dddddd.
Thanks in advance.
EDIT:
I believe that the stylesheet is not being properly applied to AppearanceTab, however I don;t know why that would be seeing as it just inherits from QWidget.
EDIT 2:
A MCVE (along with the rest of my project) can be found on github.
In the documentation, there is a paragraph the paragraph about inheritance and style:
Inheritance
In classic CSS, when font and color of an item is not explicitly set, it gets automatically inherited from the parent. When using Qt Style Sheets, a widget does not automatically inherit its font and color setting from its parent widget.
If we want to set the color on a QGroupBox and its children, we can write:
qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }");
So you probably want to to change
QMainWindowStyle = QMainWindow {color: #dddddd; background-color: #333333;}
to
QMainWindowStyle = QMainWindow, QMainWindow * {color: #dddddd; background-color: #333333;}
so that all the child widgets of the main window have the same style.
Try it:
from PyQt5 import QtWidgets
class Dialog(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.layout = QtWidgets.QGridLayout()
self.main = QtWidgets.QWidget()
self.main.setLayout(self.layout)
# self.setStyleSheet(QMainWindowStyle)
self.setCentralWidget(self.main)
self.show()
class AppearanceTab(QtWidgets.QWidget):
def __init__(self):
super().__init__()
class SettingsDialog(Dialog):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget(self)
# self.tabs.setStyleSheet(QTabWidgetStyle)
self.layout.addWidget(self.tabs)
self.tab_appearance = AppearanceTab()
# self.tab_appearance.setStyleSheet(QWidgetStyle)
## self.tab_appearance.setStyleSheet("QWidget, QWidget * {color: #dddddd; background-color: #333333;}") #note: Tried this however it didn't work.
self.tab_appearance_layout = QtWidgets.QGridLayout()
self.tab_appearance.setLayout(self.tab_appearance_layout)
self.tabs.addTab(self.tab_appearance, "Appearance")
self.tab_server = QtWidgets.QWidget()
# self.tab_server.setStyleSheet(QWidgetStyle)
self.tab_server_layout = QtWidgets.QGridLayout()
self.tab_server.setLayout(self.tab_server_layout)
self.tabs.addTab(self.tab_server, "Server")
style = """
QWidget {
color: #dddddd;
background-color: #333333;
}
QMainWindow {
color: #dddddd;
background-color: #333333;
}
QTabWidget {
background-color: #333333;
color: #dddddd;
}
QTabBar {
color: #dddddd;
background-color: #333333;
}
"""
if __name__ == "__main__":
QtWidgets.QApplication.setStyle(QtWidgets.QStyleFactory.create("Fusion"))
app = QtWidgets.QApplication([])
app.setStyleSheet(style) # < ---
d = SettingsDialog()
print(app.exec_())

Categories

Resources