PYQT5: How to remove margins from QGroupBox - python

I made three group boxes in a vertical layout. What I want is that they are right next to each other, no space in between. I set window layout margins (.setContentsMargins) and it removes all margins except the one in between them. I tried setting gbox style sheet, padding and margins to 0 but nothing happens. Any ideas?
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QGroupBox
class mainWindow(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(200, 200, 800, 600)
self.setFixedSize(800, 600)
self.winLayout = QVBoxLayout()
# ==================================================================
self.gboxFirst, self.gboxFirstLayout = self.create_v_layout()
self.gboxSecond, self.gboxSecondLayout = self.create_v_layout()
self.gboxThird, self.gboxThirdLayout = self.create_v_layout()
self.winLayout.addWidget(self.gboxFirst)
self.winLayout.addWidget(self.gboxSecond)
self.winLayout.addWidget(self.gboxThird)
self.winLayout.setContentsMargins(0,0,0,0)
# ==================================================================
# Coloring gbox-es
self.gboxFirst.setStyleSheet(""" QGroupBox {
background-color: red;
}""")
self.gboxSecond.setStyleSheet(""" QGroupBox {
background-color: green;
}""")
self.gboxThird.setStyleSheet(""" QGroupBox {
background-color: blue;
}""")
self.setLayout(self.winLayout)
self.show()
def create_v_layout(self):
gbox = QGroupBox()
gboxLayout = QVBoxLayout()
gbox.setLayout(gboxLayout)
return gbox, gboxLayout
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = mainWindow()
app.exec_()

Related

How to remove black space when deleting widgets in PyQt5

I have a code to generate a GUI with PyQt5 that enables a user to create multiple buttons (QPushButton) based on an entry (QLineEdit), and to delete these buttons when pressing an "X" button (deleteLater()).
My problem is that when deleting some of these buttons by pressing the associated X-button, this leaves a small empty space where the buttons were initially, and I therefore wonder how to remove these spaces?
Image of the empty spaces
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLineEdit, QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QGroupBox, QScrollArea, QLabel
from PyQt5.QtCore import Qt
import sys
class MyWindow(QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.setWindowTitle("My Program")
self.setGeometry(100, 100, 1500, 1500)
self.initUI()
def initUI(self):
widgets = MainWidgets()
self.setCentralWidget(widgets)
class MainWidgets(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.grid = QGridLayout()
self.grid.setColumnStretch(0, 1)
self.grid.setColumnStretch(1, 1)
self.grid.setColumnStretch(2, 1)
self.grid.setColumnStretch(3, 1)
self.grid.setColumnStretch(4, 1)
self.groupBox = QGroupBox("Labels")
self.groupBox.setStyleSheet('''
QGroupBox::title {
subcontrol-position: top center;
}
''')
right_column_layout = QVBoxLayout(self.groupBox)
scrollArea = QScrollArea()
scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scrollArea.setWidgetResizable(True)
right_column_layout.addWidget(scrollArea)
scrollArea.setWidget(RightColWidgets())
self.grid.addWidget(self.groupBox, 0, 5, 1, 5)
self.setLayout(self.grid)
class RightColWidgets(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.layout = QVBoxLayout(self)
self.labelEntry = QLineEdit(self)
self.addLabelButton = QPushButton(self)
self.addLabelButton.setText("Add Label")
self.addLabelButton.clicked.connect(self.addNewLabel)
self.emptyspace = QLabel(self)
self.layout.addWidget(self.labelEntry, stretch=0)
self.layout.addWidget(self.addLabelButton, stretch=0)
self.layout.addWidget(self.emptyspace, stretch=1)
def addNewLabel(self):
labelname = self.labelEntry.text()
newLabelItems = Labels(self, labelname)
self.layout.insertWidget(2, newLabelItems)
class Labels(QWidget):
def __init__(self, parent, labelname, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.mylabelname = labelname
self.initUI()
def initUI(self):
self.labelButton = QPushButton(self)
self.labelButton.setText(str(self.mylabelname))
self.labelButton.setStyleSheet("""
QPushButton {border: 1px solid back; background: rgba(103, 186, 181, 0.5); padding-top: 10px; padding-bottom: 10px}
""")
self.labelButton.clicked.connect(self.printbutton)
self.buttonErase = QPushButton(self)
self.buttonErase.setText("X")
self.buttonErase.setStyleSheet("""
QPushButton {border: 1px solid back; padding-right: 5 px; padding-left: 5 px; padding-top: 10px; padding-bottom: 10px}
""")
self.buttonErase.clicked.connect(self.erasebutton)
layout = QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.labelButton, stretch=1)
layout.addWidget(self.buttonErase, stretch=0)
def printbutton(self):
print('clicked:', self.labelButton.text())
def erasebutton(self):
self.labelButton.deleteLater()
self.buttonErase.deleteLater()
if __name__ == '__main__':
app = QApplication(sys.argv)
# app.setStyle('Fusion')
window = MyWindow()
window.showMaximized()
sys.exit(app.exec_())
Deleting the children does not delete the container, so what you see is the empty Labels widget with the spacing of its layout contentsMargins().
A simple solution could be to directly connect the button with its own deleteLeter(), which automatically deletes its children:
self.buttonErase.clicked.connect(self.deleteLater)
A better solution would be to connect the signal to the parent and let it do everything necessary in a cleaner way, as you might need to keep track of the existing widgets (for instance, to remove them from the list of currently existing labels):
class RightColWidgets(QWidget):
# ...
def addNewLabel(self):
labelname = self.labelEntry.text()
newLabelItems = Labels(self, labelname)
self.layout.insertWidget(2, newLabelItems)
newLabelItems.buttonErase.clicked.connect(
lambda: self.deleteLabel(newLabelItems))
def deleteLabel(self, widget):
self.layout.removeWidget(widget)
widget.deleteLater()
Obviously, in this case you don't need to connect the clicked signal in the initUi of the Label class anymore.
Note that layout() is an existing (and dynamic) property of any QWidget, so you should not overwrite it.

How to change the background color of a QScrollArea without affecting the scrollbar color?

I'm using
self.setStyleSheet("background-color: white")
to change the background color of a QScrollArea in PyQt5, but that also affects the scroll bar. What is the proper way of just changing the area background color?
import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QVBoxLayout, QScrollArea)
class TaskListWidget(QScrollArea):
def __init__(self):
super().__init__()
self.content = QWidget()
self.layout = QVBoxLayout(self.content)
for _ in range(20):
self.layout.addWidget(QLabel("task"))
self.setWidget(self.content)
self.setStyleSheet("background-color: white")
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.tasklist = TaskListWidget()
self.windowLayout = QVBoxLayout()
self.windowLayout.addWidget(self.tasklist)
self.setLayout(self.windowLayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
One possible solution is to set the background-color of the QScrollBar to None.
self.setStyleSheet(
"""
QWidget{ background-color: white }
QScrollBar{ background-color: none }
"""
)

pyqt4 widget appear outside the layout

I want the effect in the picture. The qlabel is in the layout, and the font is centered. Below is the code I wrote, which is different from what I want. First, qlabel is outside the layout, and second, the font is not centered.
import sys
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QLabel, QApplication, QWidget, QVBoxLayout, QStyleOption, QPainter, QStyle
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.setGeometry(100, 100, 500, 500)
vbox = QVBoxLayout()
self.setLayout(vbox)
a = QLabel('aaa')
b = QLabel('bbb')
vbox.addWidget(a)
vbox.addWidget(a, alignment=Qt.AlignHCenter | Qt.AlignTop)
vbox.addWidget(b, alignment=Qt.AlignHCenter | Qt.AlignBottom)
self.setStyleSheet("""
Example{
border-width: 1px;
border-style: solid;
border-color: red;
min-width:500px;
max-width:500px;
min-height:500px;
max-height:500px;
padding:1px 1px 1px 1px;
margin-bottom:30px;
}
QLabel{
min-width:500px;
max-width:500px;
min-height:50px;
max-height:50px;
margin:0px;
padding:0px;
background-color:#cdcdcd;
text-align: center center;
}
""")
self.show()
def paintEvent(self, event):
super(Example, self).paintEvent(event)
opt = QStyleOption()
opt.initFrom(self)
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The logic of the layouts is to handle the geometry of the elements but you are also handling the geometry with the stylesheet, causing the problem you observe. On the other hand, the QLabel does not have the text-align property but you have to use qproperty-alignment: AlignCenter;.
To avoid this it is better to avoid managing the geometry with stylesheet but with the methods of the class, considering the solution is:
import sys
from PyQt4.QtCore import Qt
from PyQt4.QtGui import (
QLabel,
QApplication,
QWidget,
QVBoxLayout,
QStyleOption,
QPainter,
QStyle,
)
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.setGeometry(100, 100, 500, 500)
self.label_a = QLabel("aaa")
self.label_b = QLabel("bbb")
self.label_a.setFixedHeight(50)
self.label_b.setFixedHeight(50)
vbox = QVBoxLayout(self)
vbox.setContentsMargins(1, 1, 1, 1)
vbox.addWidget(self.label_a)
vbox.addWidget(QWidget(), stretch=1)
vbox.addWidget(self.label_b)
self.setFixedSize(500, 500)
self.setStyleSheet(
"""
Example{
border-width: 1px;
border-style: solid;
border-color: red;
padding:1px 1px 1px 1px;
}
QLabel{
margin:0px;
padding:0px;
background-color:#cdcdcd;
qproperty-alignment: AlignCenter;
}
"""
)
def paintEvent(self, event):
super(Example, self).paintEvent(event)
opt = QStyleOption()
opt.initFrom(self)
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

QScroller on QListWidget in QTabWidget is not work

In nutshell, I have QTabWidget which contain tab1 and tab2. Each of tab1 and tab2 contains a QListWidget. I would like to use QScroller on each of the QListWidget.
Here is the code (The simplest version).:
import sys
from PyQt5.QtWidgets import (QWidget, QHBoxLayout, QAbstractItemView, QScrollerProperties, QScroller, QVBoxLayout, QListWidget,
QTabWidget, QApplication, QLabel, QListWidgetItem)
from PyQt5.QtCore import Qt
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.resize(700, 300)
mainLayout = QVBoxLayout()
self.tabWidget = QTabWidget()
self.tabWidget.setStyleSheet("QTabBar::tab { height: 50px; width: 250px; }")
mainLayout.addWidget(self.tabWidget)
myBoxLayout = QHBoxLayout()
self.tabWidget.setLayout(myBoxLayout)
self.tab1 = WidgetTab1()
self.tab2 = WidgetTab2()
self.tabWidget.addTab(self.tab1, 'Tab1')
self.tabWidget.addTab(self.tab2, 'Tab2')
self.setLayout(mainLayout)
class WidgetTab1(QWidget):
def __init__(self):
super().__init__()
self.hbox = QHBoxLayout()
# Create the list
self.mylist = QListWidget()
self.mylist.setStyleSheet("QListWidget::item { border-bottom: 1px solid gray; }")
self.mylist.setFocusPolicy(Qt.NoFocus)
self.mylist.setSelectionMode(QAbstractItemView.NoSelection)
self.mylist.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.mylist.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
for i in range(20):
item = QListWidgetItem(self.mylist)
self.mylist.addItem(item)
self.mylist.setItemWidget(item, QLabel(str(i)))
self.sp = QScrollerProperties()
self.sp.setScrollMetric(QScrollerProperties.DragVelocitySmoothingFactor, 0.6)
self.sp.setScrollMetric(QScrollerProperties.MinimumVelocity, 0.0)
self.sp.setScrollMetric(QScrollerProperties.MaximumVelocity, 0.2)
self.sp.setScrollMetric(QScrollerProperties.AcceleratingFlickMaximumTime, 0.1)
self.sp.setScrollMetric(QScrollerProperties.AcceleratingFlickSpeedupFactor, 1.2)
self.sp.setScrollMetric(QScrollerProperties.SnapPositionRatio, 0.2)
self.sp.setScrollMetric(QScrollerProperties.MaximumClickThroughVelocity, 1)
self.sp.setScrollMetric(QScrollerProperties.DragStartDistance, 0.001)
self.sp.setScrollMetric(QScrollerProperties.MousePressEventDelay, 0.5)
self.scroller = QScroller.scroller(self.mylist.viewport())
self.scroller.setScrollerProperties(self.sp)
self.scroller.grabGesture(self.mylist.viewport(), QScroller.LeftMouseButtonGesture)
self.mylist.show()
self.hbox.addWidget(self.mylist)
self.setLayout(self.hbox)
class WidgetTab2(QWidget):
def __init__(self):
super().__init__()
self.hbox = QHBoxLayout()
# Create the list
self.mylist = QListWidget()
self.mylist.setStyleSheet("QListWidget::item { border-bottom: 1px solid gray; }")
self.mylist.setFocusPolicy(Qt.NoFocus)
self.mylist.setSelectionMode(QAbstractItemView.NoSelection)
self.mylist.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.mylist.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
for i in range(19, 0, -1):
item = QListWidgetItem(self.mylist)
self.mylist.addItem(item)
self.mylist.setItemWidget(item, QLabel(str(i)))
self.sp = QScrollerProperties()
self.sp.setScrollMetric(QScrollerProperties.DragVelocitySmoothingFactor, 0.6)
self.sp.setScrollMetric(QScrollerProperties.MinimumVelocity, 0.0)
self.sp.setScrollMetric(QScrollerProperties.MaximumVelocity, 0.2)
self.sp.setScrollMetric(QScrollerProperties.AcceleratingFlickMaximumTime, 0.1)
self.sp.setScrollMetric(QScrollerProperties.AcceleratingFlickSpeedupFactor, 1.2)
self.sp.setScrollMetric(QScrollerProperties.SnapPositionRatio, 0.2)
self.sp.setScrollMetric(QScrollerProperties.MaximumClickThroughVelocity, 1)
self.sp.setScrollMetric(QScrollerProperties.DragStartDistance, 0.001)
self.sp.setScrollMetric(QScrollerProperties.MousePressEventDelay, 0.5)
self.scroller = QScroller.scroller(self.mylist.viewport())
self.scroller.setScrollerProperties(self.sp)
self.scroller.grabGesture(self.mylist.viewport(), QScroller.LeftMouseButtonGesture)
self.mylist.show()
self.hbox.addWidget(self.mylist)
self.setLayout(self.hbox)
if __name__ == '__main__':
qApplication = QApplication(sys.argv)
window = MainWindow()
window.show()
qApplication.exec_()
The problem is, the QScroller only work in the first tab. For example, If I choose tab1 at first and scroll on it. Then I switch to tab2, the scroll on tab2 will not work. If I choose tab2 first and scroll on it. Then I switch to tab1, the scroll on tab1 will not work.
I also have tried to ungrabGesture every I switch the tab, but it does not work.
Am I implement the QScroller in the wrong way?
Thanks #G.M. for your comment.
I solve the problem by upgrading the PyQt version from 5.11.3 to 5.13.0

How to make border of QLabel look like border of other widgets like QTreeWidget?

I noticed different border styles for QTreeWidget and QLabel - even if I try to adjust the stylesheet. Of course, I could change the stylesheet for both, but ideally I'd like to keep the QTreeWidget's border style. How can I make the border of QLabel look like the border of QTreeWidget?
MCVE snippet:
import sys
from PyQt5.QtWidgets import *
class widget(QWidget):
def __init__(self):
super().__init__()
treewidget = QTreeWidget(self)
label = QLabel(self)
label.setStyleSheet("background-color: white; border: 1px inset grey; min-height: 200px;")
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(treewidget, 1, 0)
grid.addWidget(label, 2, 0)
self.setLayout(grid)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
f = widget()
sys.exit(app.exec_())
Without stylesheet:
Screenshot of resulting window for MCVE snippet:
What I want the window to look like:
You may have found the answer to this already but just in case...
The following should provide you with what you want...
label = QLabel(self)
label.setFrameShape(QFrame.Panel)
label.setFrameShadow(QFrame.Sunken)
label.setLineWidth(3)
Where I've just hardwired the line width. Note that the "border: 1px" specifier must be removed from the style sheet otherwise it will be used in preference to the specified line width. The complete code would be...
import sys
from PyQt5.QtWidgets import *
class widget(QWidget):
def __init__(self):
super().__init__()
treewidget = QTreeWidget(self)
label = QLabel(self)
label.setStyleSheet("background-color: white; inset grey; min-height: 200px;")
label.setFrameShape(QFrame.Panel)
label.setFrameShadow(QFrame.Sunken)
label.setLineWidth(3)
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(treewidget, 1, 0)
grid.addWidget(label, 2, 0)
self.setLayout(grid)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
f = widget()
sys.exit(app.exec_())

Categories