I have a QDockWidget and I have put some widgets on it's titlebar like in this MRE:
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (QWidget, QCheckBox, QMainWindow, QLabel, QDockWidget, QApplication, QHBoxLayout,
QSizePolicy)
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
central_widget = QLabel("Window stuff!")
self.setCentralWidget(central_widget)
self.dock = QDockWidget()
self.docker_layout = QHBoxLayout()
self.docker_layout.setContentsMargins(0, 0, 0, 0)
self.docker_layout.setAlignment(Qt.AlignLeft)
container = QWidget()
container.setLayout(self.docker_layout)
label = QLabel("Docker name")
label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
self.dock.setTitleBarWidget(container)
# Widgets creation:
self.docker_layout.addWidget(label)
self.check_1 = QCheckBox("Check 1")
self.docker_layout.addWidget(self.check_1)
self.check_2 = QCheckBox("Check 2", checked=True)
self.docker_layout.addWidget(self.check_2)
self.check_3 = QCheckBox("Check 3", checked=True)
self.docker_layout.addWidget(self.check_3)
self.check_4 = QCheckBox("You cant hide me haha!", checked=True)
self.docker_layout.addWidget(self.check_4)
self.dock.setTitleBarWidget(container)
self.addDockWidget(Qt.RightDockWidgetArea, self.dock)
self.dock_content = QLabel("Dock stuff!")
self.dock.setWidget(self.dock_content)
self.show()
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
app.exec()
But as you can see in the following giph, the dock is not collapsable due to the widgets in the sidebar. How can I allow the widgets to get out of screen and resize the dock freely?
That depends by the fact that the dock widget sets the minimum width based on that of the title bar widget: if no explicit minimum width is set, then the width returned by minimumSizeHint() is used.
A possible solution is to subclass the layout and reimplement both minimumWidth() and setGeometry().
The first ensures that the title bar can be resized to a smaller width, while the second lays outs the items by ignoring the given geometry at all.
The result is, obviously, that part of the title bar will be hidden.
class IgnoreWidthLayout(QHBoxLayout):
def minimumSize(self):
return QSize(80, super().minimumSize().height())
def setGeometry(self, geometry):
geometry.setWidth(super().minimumSize().width())
super().setGeometry(geometry)
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.docker_layout = IgnoreWidthLayout()
Note that:
setting the Minimum size policy on the label is not required in this situation (and you probably should need Maximum instead, consider that QSizePolicy flags have names that are unintuitive at first);
you're calling setTitleBarWidget twice;
setting the alignment on the layout has usually little use (and is almost useless in this specific case): it doesn't tell the alignment of the child items, it specifies the alignment that layout manager will have once it's set on a parent widget or added to another layout;
Related
I am trying to stop a widget from expanding by setting its size policy but things are not working.
The following code runs:
from PyQt5.QtWidgets import*
import pyqtgraph as pg
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QHBoxLayout()
self.left_widget = pg.GraphicsLayoutWidget()
self.layout.addWidget(self.left_widget)
self.right_widget = RightWidget()
#self.right_widget.groupbox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
self.layout.addWidget(self.right_widget)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
class RightWidget(QWidget):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.groupbox = QGroupBox()
self.groupbox.setTitle("some title")
#self.groupbox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
self.groupboxlayout = QVBoxLayout()
self.button1 = QPushButton ("1")
self.groupboxlayout.addWidget(self.button1)
self.button2 = QPushButton ("2")
self.groupboxlayout.addWidget(self.button2)
self.button3 = QPushButton ("3")
self.groupboxlayout.addWidget(self.button3)
self.groupbox.setLayout(self.groupboxlayout)
self.layout.addWidget(self.groupbox)
self.button4 = QPushButton ("4")
self.layout.addWidget(self.button4)
self.button5 = QPushButton ("5")
self.layout.addWidget(self.button5)
self.setLayout(self.layout)
if __name__ == '__main__':
import sys
system_app = QApplication(sys.argv)
main = MainWindow()
main.show()
system_app.exec()
It gives:
The specific choice of widgets should not matter. The point is that, there is a large widget on the left and there are smaller widgets on the right that is not large enough to fill the column. I want to modify the above code to shrink the widgets on the right hand side. That is:
In words, I want the widgets to shrink to top and bottom of the page in a way taking minimum space (and hence leaving the middle empty).
I attempted to use QSizePolicy to achieve the desired result. The commented lines seem to have no impact on the page at all. I don't know what went wrong.
I created the following windows which generally works fine:
from PyQt5.QtWidgets import (QApplication, QComboBox, QDialog,
QDialogButtonBox, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout,
QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, QSpinBox, QTextEdit,
QVBoxLayout)
import sys
class Dialog(QDialog):
NumGridRows = 3
NumButtons = 4
def __init__(self):
super(Dialog, self).__init__()
self.createFormGroupBox()
self.Button1 = QPushButton(self)
self.Button1.setText("Calc")
self.Button2 = QPushButton(self)
self.Button2.setText("Reset")
# buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
# buttonBox.accepted.connect(self.accept)
# buttonBox.rejected.connect(self.reject)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.formGroupBox)
# mainLayout.addWidget(buttonBox)
mainLayout.addWidget(self.Button1)
mainLayout.addWidget(self.Button2)
self.setLayout(mainLayout)
self.setWindowTitle("Form Layout")
def createFormGroupBox(self):
self.formGroupBox = QGroupBox("Form layout")
layout = QFormLayout()
layout.addRow(QLabel("Name:"), QLineEdit())
layout.addRow(QLabel("Country:"), QComboBox())
layout.addRow(QLabel("Age:"), QSpinBox())
self.formGroupBox.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = Dialog()
sys.exit(dialog.exec_())
The only thing I would like to change is that the buttons are horizontally arranged under the form layout and not vertically.
How can I achieve that to mix a vertical box with a horizontally box inside?
A Qt layout manager is a "container" of QLayoutItems, which are abstract items that represent visual objects that can be layed out.
Those items usually contain a Qt widget, but they can also be other objects, like spacers (QSpacerItem) or even other layouts (note that QLayout actually inherits from QLayoutItem).
You can actually do nested layouts.
The solution to your problem is actually pretty easy:
create a horizontal layout;
add the buttons to that layout;
add the layout to the main layout;
class Dialog(QDialog):
NumGridRows = 3
NumButtons = 4
def __init__(self):
super(Dialog, self).__init__()
self.setWindowTitle("Form Layout")
self.createFormGroupBox()
# QLayout(widget) automatically sets the layout on "widget" so you
# don't need to call widget.setLayout()
mainLayout = QVBoxLayout(self)
mainLayout.addWidget(self.formGroupBox)
self.button1 = QPushButton("Calc")
self.button2 = QPushButton("Reset")
buttonLayout = QHBoxLayout()
buttonLayout.addWidget(self.button1)
buttonLayout.addWidget(self.button2)
mainLayout.addLayout(buttonLayout)
Notes:
when widgets are going to be added to a layout, there's no point in providing the widget argument (self, in this case), as adding them to a layout that is set on a widget automatically reparents them;
only classes and constants should have capitalized names, not variables, functions or instance attributes (that's why I changed button1 and button2); remember that readability is very important, see the official Style Guide for Python Code to know more;
QDialogButtonBox is a QWidget on its own, with its internal layout; you can still use it for custom buttons, though; for instance, you could change the text of the Ok button doing buttonBox.button(buttonBox.Ok).setText("Calc"); or you could add a custom button by doing okButton = QPushButton("Calc") and buttonBox.addButton(okButton, buttonBox.AcceptRole); please carefully study the documentation to better understand the behavior of that class;
I'm trying to setup a layout similar to this question. Create Qt layout with fixed height. I'm trying to use the solution posted there but I cant seem to recreate it in python. How do I create a widget with a horizontal box layout and set the dimensions of the new widget? When I tried recreating the code in python my widget ended up as a layout instead of a widget. Below is the code I'm trying to rewrite in python, thanks in advance:
// first create the four widgets at the top left,
// and use QWidget::setFixedWidth() on each of them.
// then set up the top widget (composed of the four smaller widgets):
QWidget *topWidget = new QWidget;
QHBoxLayout *topWidgetLayout = new QHBoxLayout(topWidget);
topWidgetLayout->addWidget(widget1);
topWidgetLayout->addWidget(widget2);
topWidgetLayout->addWidget(widget3);
topWidgetLayout->addWidget(widget4);
topWidgetLayout->addStretch(1); // add the stretch
topWidget->setFixedHeight(50);
// now put the bottom (centered) widget into its own QHBoxLayout
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addStretch(1);
hLayout->addWidget(bottomWidget);
hLayout->addStretch(1);
bottomWidget->setFixedSize(QSize(50, 50));
// now use a QVBoxLayout to lay everything out
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(topWidget);
mainLayout->addStretch(1);
mainLayout->addLayout(hLayout);
mainLayout->addStretch(1);
The translation of C++ to Python does not have so much science since you only have to understand the logic:
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QApplication, QComboBox, QHBoxLayout, QLineEdit, QPushButton, QSpinBox, QToolButton, QVBoxLayout, QWidget
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
widget1 = QPushButton("widget1")
widget2 = QSpinBox()
widget3 = QComboBox()
widget4 = QLineEdit()
bottomWidget = QToolButton(text="botton")
# first create the four widgets at the top left,
# and use QWidget::setFixedWidth() on each of them.
# then set up the top widget (composed of the four smaller widgets):
topWidget = QWidget()
topWidgetLayout = QHBoxLayout(topWidget)
topWidgetLayout.addWidget(widget1)
topWidgetLayout.addWidget(widget2)
topWidgetLayout.addWidget(widget3)
topWidgetLayout.addWidget(widget4)
topWidgetLayout.addStretch(1)
topWidget.setFixedHeight(50)
# now put the bottom (centered) widget into its own QHBoxLayout
hLayout = QHBoxLayout()
hLayout.addStretch(1)
hLayout.addWidget(bottomWidget)
hLayout.addStretch(1)
bottomWidget.setFixedSize(QSize(50, 50))
# now use a QVBoxLayout to lay everything out
mainLayout = QVBoxLayout()
mainLayout.addWidget(topWidget)
mainLayout.addStretch(1)
mainLayout.addLayout(hLayout)
mainLayout.addStretch(1)
self.setLayout(mainLayout)
self.resize(640, 480)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I have a toplevel widget with a layout set and 2 other widgets added to that layout, when I color them all I only see the toplevel widget color but Id like to see the children widgets on top. This is what I have attempted but it just displays a blue QWidget, I am expecting red and green widgets one on top of the other
def set_color(widget, color):
p = widget.palette()
p.setColor(widget.backgroundRole(), color)
widget.setPalette(p)
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget,QVBoxLayout
app = QApplication([])
win = QWidget()
win.show()
win.resize(1920,1080)
vlayout = QVBoxLayout()
win.setLayout(vlayout)
set_color(win, Qt.blue)
mod_group = QWidget()
mod_layout = QHBoxLayout()
mod_group.setLayout(mod_layout)
vlayout.addWidget(mod_group)
set_color(mod_group, Qt.red)
mod_group.show()
audio_group = QWidget()
audio_layout = QHBoxLayout()
vlayout.addWidget(audio_group)
audio_group.setLayout(audio_layout)
set_color(audio_group, Qt.green)
audio_group.show()
The widgets are visible but the background color you use is the same as the parent widget, so that they are applied correctly you must enable the autoFillBackground property:
mod_group.setAutoFillBackground(True)
audio_group.setAutoFillBackground(True)
I have this code:
class Window(QWidget):
def __init__(self):
super().__init__()
def init_gui(self):
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.new1()
self.new2()
self.showMaximized()
def create_scroll_area(self):
scroll_area = QScrollArea()
widget = QWidget()
scroll_area.setWidget(widget)
layout = QVBoxLayout()
widget.setLayout(layout)
button = QPushButton("Ahoj")
layout.addWidget(button)
self.layout.addLayout(layout)
def new1(self):
self.create_scroll_area()
def new2(self):
self.create_scroll_area()
I get this error message:
QLayout::addChildLayout: layout "" already has a parent
What's wrong?
Who is layout's parent? Widget? I also tried self.widget instead of widget and it still does not work.
Please try this code.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Window(QWidget):
def __init__(self, parent=None):
super().__init__(parent=None)
self.init_gui()
def init_gui(self):
self.create_scroll_area()
self.showMaximized()
def create_scroll_area(self):
scroll_area = QScrollArea()
widget = QWidget()
layout = QVBoxLayout()
button = QPushButton("Ahoj")
layout.addWidget(button)
widget.setLayout(layout)
scroll_area.setWidget(widget)
self.setLayout(layout)
def main():
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Let's revise from A to Z.
1 to write self.init_gui() in __init__ constructor.
If you don't do it, you can't execute init_gui method at the first time.
2.setLayout() or setWidget() should be written at the last place at least.
In python, we prepare the things we want to show, and set them on the mainwidget, and show them at the last time.
3.please pay attention to self.layout name.
Widget has originally setLayout() method. and layout() method.
If you make self.layout = ***, you crush the original method of QWidget.
4. it may as well delete new1 and new2 method.
please call them directly.
5. please look create_scroll_area method.
You make three widget.QScrollArea,QWidget,QPushButton.
and make a layout object.and you set the layout into QWidget.
But you set the QWidget before the widget set the layout.
It is not good order for coding.
You make QPushButton but the button doesn't belong to any widget.
Because you set the button on the self.layout certainly, but if you want to show it, you must setLayout(self.layout) at the last position.