Python PyQt5 I wanna set the position of my Grid Layout - python

I need to set the position of my Grid Layout. It has to be exactly where I want it to be. I've tried some methods like ".setGeometry, .setAlignment" and I couldn't unfortunately have the result exactly I wanted.
As you can see from this photo, I want it to be in the bottom right but I guess there's no method for it. (I need to say that the interface in the photo below is a different interface. I don't need this interface. Because of I wanted to show you exactly what kind of stuff I wanted.)

You can achieve this using layouts and their setStretch methods.
Here is a runnable example:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MainWindow(QMainWindow):
def __init__(self, parent=None) -> None:
super().__init__(parent)
self.central = QWidget()
# create a vertical layout and add stretch so it is the first
# item of the layout and everything that is inserted after
# is pushed down to the bottom
self.layout = QVBoxLayout(self.central)
self.layout.addStretch()
self.btn1 = QPushButton("PushButton", self)
self.btn2 = QPushButton("PushButton", self)
self.btn3 = QPushButton("PushButton", self)
# create a horizontal layout and add stretch so everything is
# pushed to the right
self.hlayout = QHBoxLayout()
self.hlayout.addStretch()
# add buttons after the stretch so they will be pushed to the
# right
self.hlayout.addWidget(self.btn1)
self.hlayout.addWidget(self.btn2)
self.hlayout.addWidget(self.btn3)
# add horizontal layout to vertical layout which will be pushed
# to the bottom from the vertical layouts stretch
self.layout.addLayout(self.hlayout)
self.setCentralWidget(self.central)
app = QApplication([])
window = MainWindow()
window.show()
app.exec_()
The alternative would be to use the QHBoxLayout.setgeometry(QRect(x, y, w, h)) but this will force the layout into a specific position that will not adjust dynamically if you resize the window, or for any other reason.
This is essentially what is happening in the code above. The blue lines would represent the stretch. the stretch can be thought of as invisible pressure that can be inserted into any layout, before, after, or between any widgets, It can work in either direction.
This same process can be done using QGridWidget.

Related

Stacked Layout causing alignment to top problem

I have a vertical layout with alignment set to "top" that contains what I want to be a fixed height "header" bar contained in a QHBoxLayout, with a QTextEdit window underneath it. One of the sections of this header bar contains a QStackedLayout. If you run the sample code below, when resizing the window the QTextEdit window begins pulling away from the "header" bar instead of staying anchored just underneath the "header" bar. The intended behavior would be for the QTextEdit window to remain anchored just underneath the header bar and expand and contract along its bottom margin. How do I fix the size of the QStackedLayout element to achieve this behavior?
from PySide2 import QtWidgets, QtCore, QtGui
from PySide2.QtWidgets import QApplication
import sys
class Demo(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Demo, self).__init__(parent)
self.main_layout = QtWidgets.QVBoxLayout(self)
self.main_layout.setAlignment(QtCore.Qt.AlignTop)
# Header with combobox anchored to the top of the layout
self.data_results_layout = QtWidgets.QHBoxLayout()
self.results_stacked_layout = QtWidgets.QStackedLayout()
self.data_results_layout.addLayout(self.results_stacked_layout)
self.combobox_results_widget = QtWidgets.QWidget()
self.combobox_results_widget.setFixedHeight(25)
self.combobox_results_layout = QtWidgets.QVBoxLayout(self.combobox_results_widget)
self.combobox_results_layout.setContentsMargins(0,0,0,0)
self.directory_combobox = QtWidgets.QComboBox()
self.directory_combobox.setFixedHeight(25)
self.combobox_results_layout.addWidget(self.directory_combobox)
self.results_stacked_layout.addWidget(self.combobox_results_widget)
self.main_layout.addLayout(self.data_results_layout)
# QTextEdit Window
self.asset_data_layout = QtWidgets.QVBoxLayout()
self.asset_data_window = QtWidgets.QTextEdit('Ready.')
self.asset_data_layout.addWidget(self.asset_data_window)
self.main_layout.addLayout(self.asset_data_layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
Setting the alignment on a layout does not set the alignment of the widgets it manages, but only the alignment that that layout will have when added to another layout manager (you can read a more deeper explanation in this answer).
Basically, the following:
layout.setAlignment(QtCore.Qt.AlignTop)
is almost the same as this:
otherLayout.addLayout(layout, alignment=QtCore.Qt.AlignTop)
So, not only that alignment won't affect the children alignment, but since you're using that layout as main layout for the widget, that alignment is completely ignored.
In order to achieve what you want you can set a stretch for the lower alignment (as suggested in the comment by S.Nick):
self.main_layout.addLayout(self.asset_data_layout, stretch=1)
On the other hand, consider that QStackedLayout should only used for custom widgets or dedicated behavior, and in most cases it's better to use its convenience class QStackedWidget, on which you can set an appropriate size policy:
self.results_stacked_layout = QtWidgets.QStackedWidget()
self.data_results_layout.addWidget(self.results_stacked_layout)
self.results_stacked_layout.setSizePolicy(
QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
With the above, the stacked widget will resize itself horizontally according to its contents and will only use the minimum vertical space required by it: since the combobox expands horizontally as much as possible, it will try to occupy all the horizontal available space, and since the vertical policy of the combobox is fixed, it will only use the vertical space required, leaving all the remaining vertical space to the other widgets in the parent layout.
Note that the size policy definitions might seem a bit counterintuitive; the rule is that it's always referred to the sizeHint of the widget: Maximum means that the size hint of the contents will be used as maximum size, and it cannot be larger than that.

How to push widgets to top left instead of bottom right

Just started messing with pyqt5 and was wondering how to push the widgets to top for QVBoxLayout and left for QHBoxLayout. Currently with addStretch() it pushes the widgets to bottom/right. I'd like to avoid using absolute positioning.
self.tabColor.layout = QVBoxLayout(self)
self.tabColor.layout.addStretch()
## FOR CONTROL
lbl_control = QLabel(self)
lbl_control.setText("control")
le_control = QLineEdit(self)
hbox_control = QHBoxLayout()
hbox_control.addStretch(1)
hbox_control.addWidget(lbl_control)
hbox_control.addWidget(le_control)
## FOR UNKNOWN
lbl_unknown = QLabel(self)
lbl_unknown.setText("unknown")
le_unknown = QLineEdit(self)
hbox_unknown = QHBoxLayout()
hbox_unknown.addStretch(1)
hbox_unknown.addWidget(lbl_unknown)
hbox_unknown.addWidget(le_unknown)
self.tabColor.layout.addLayout(hbox_control)
self.tabColor.layout.addLayout(hbox_unknown)
self.tabColor.setLayout(self.tabColor.layout)
I used self.tabColor.layout.setDirection(3). It places the horizontal layout on top but it seems to flip the order. Basically 'unknown' will be first then 'conrol'. Its not a big deal but was wondering if there are other options i could try
The first approach I'd recommend you would also be to set the direction of your layout, since you are trying to have another way:
Try adding always in the beginning, instead of adding widget trying inserting it
instead of:
self.layout.addWidget(self.lbl_1)
self.layout.addWidget(self.lbl_2)
self.layout.addWidget(self.lbl_3)
try:
self.layout.insertWidget(0,self.lbl_1)
self.layout.insertWidget(0,self.lbl_2)
self.layout.insertWidget(0,self.lbl_3)
and it'll be added in the opposite direction, the widgets that are already inside your layout will be shifted according to your layout QHBoxLayout, or QVBoxLayout.
Change the order of addWidget/Stretch calls (i.e. add the stretch after the widget)

How to add scrollbars in PyQt4 WITHOUT with absolute positioning

I'm creating an interface in PyQt4, and I am in need of scrollbars. My interface uses absolute positioning. I have looked a other treads for adding scrollbars, but the only answers given are to those interfaces without a layout (such as VBoxLayout, Grid Layout, etc).
Please take a look at my code. How could I add a scrollbars (horizontal and vertical) to this interface?
The full code wouldn't format properly on here, so I'll link to this pastebin
http://pastebin.com/hEH4R534
Here is the base of the interface (a 1500px by 1000px window)
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(100,100,1500,1000)
def main():
import sys
app = QtGui.QApplication(sys.argv)
window = Example()
window.show()
sys.exit(app.exec_())
main()
The question is... How would I modify the code above so I have horizontal and vertical scrollbars?
Only widgets that inherit from QAbstractScrollArea can have scroll bars. You could place one of those widgets inside your widget, and then place other widgets inside that widget. Or, just have your widget inherit from QScrollArea instead of QWidget.
By default, scroll bars will only appear when necessary to display hidden child widgets. You can force scroll bars to appear by setting the scroll bar policy for the widget.
from PyQt4.QtCore import Qt
...
def initUI(self):
self.setGeometry(100, 100, 1500, 1000)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

QBoxLayouts with Python

Because of their high customizability I've been relying on using multiple GroupBoxes while building app GUIs. But it appears QGroupBoxes make a certain impact on how fast an interface builds.
Now with layout's .insertLayout() method I can build an entire graphics interface placing the widgets any where I want. The dialogs feel very lightweight and extremely fast to re-draw. Unfortunately I can't find a way to control their appearance. I would appreciate if you would give me some clue on how to control the layout visual properties. I am particularly interested in knowing:
How to draw layout border, how to control a border line width,
How to place a layout title (similar to what QGroupBox's .setTitle() does)
How to control the layout outside and inside margins.
How to make layout minimizable/size-restorable (So the user could click some minus/arrow icon to fold/unfold layout when they need or don't need certain widgets belonging to the same layout.
The example with three nested layouts is posted below. As it is seen on dialog screenshot there is no way to visually differentiate one layout from another since there are no border, no titles, no dividers and etc.
from PyQt4 import QtGui, QtCore
class Dialog_01(QtGui.QMainWindow):
def __init__(self):
super(QtGui.QMainWindow,self).__init__()
tabWidget = QtGui.QTabWidget()
tabGroupBox = QtGui.QGroupBox()
tabLayout = QtGui.QVBoxLayout()
tabLayout.setContentsMargins(0, 0, 0, 0)
tabLayout.setSpacing(0)
subLayoutA=QtGui.QVBoxLayout()
tabLayout.insertLayout(0, subLayoutA)
tabGroupBox.setLayout(tabLayout)
tabWidget.addTab(tabGroupBox,' Tab A ')
listWidgetA = QtGui.QListWidget()
for i in range(3):
QtGui.QListWidgetItem( 'Item '+str(i), listWidgetA )
subLayoutA.addWidget(listWidgetA)
subLayoutB=QtGui.QHBoxLayout()
tabLayout.insertLayout(1, subLayoutB)
subLayoutB.addWidget(QtGui.QLineEdit('LineEdit 1'))
subLayoutB.addWidget(QtGui.QLineEdit('LineEdit 2'))
subLayoutC=QtGui.QVBoxLayout()
tabLayout.insertLayout(2, subLayoutC)
subLayoutC.addWidget(QtGui.QPushButton('PushButton 1'))
subLayoutC.addWidget(QtGui.QPushButton('PushButton 2'))
self.setCentralWidget(tabWidget)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
dialog_1.resize(480,320)
sys.exit(app.exec_())
EDITED LATER
I inserted two lines into an example code to implement one of the suggestions made by sebastian. A Spacing-Margins method combos can be effectively used to get some additional tweaks done. Here is a screenshot (still could not get rid of the spacing around pushButtons):
QLayout sublcasses don't have a visual representation, which becomes clear by the fact that QLayout classes do not inherit QWidget. They only calculate the positions of the widgets they are responsible for in the context of their "parent" widget.
So the answer to questions 1,2 and 4 is basically: You can't.
You'll always have to have a QWidget in combination with a QLayout.
E.g. to group your two buttons into a frame with a box use a QFrame:
subLayoutC=QtGui.QVBoxLayout()
buttonFrame = QtGui.QFrame()
buttonFrame.setFrameStyle(QtGui.QFrame.Plain |QtGui.QFrame.Box)
buttonFrame.setLayout(subLayoutC)
subLayoutC.addWidget(QtGui.QPushButton('PushButton 1'))
subLayoutC.addWidget(QtGui.QPushButton('PushButton 2'))
# now we add the QFrame widget - not subLayoutC to the tabLayout
tabLayout.addWidget(buttonFrame) # I think your suggested edit was correct here
self.setCentralWidget(tabWidget)
Concerning question 3, check the docs:
http://qt-project.org/doc/qt-4.8/qlayout.html#setContentsMargins
http://qt-project.org/doc/qt-4.8/qboxlayout.html#setSpacing

PyQt: getting widgets to resize automatically in a QDialog

I'm having difficulty getting widgets in a QDialog resized automatically when the dialog itself is resized.
In the following program, the textarea resizes automatically if you resize the main window. However, the textarea within the dialog stays the same size when the dialog is resized.
Is there any way of making the textarea in the dialog resize automatically? I've tried using setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) on the dialog itself and the two widgets within, but that seems to have no effect.
I'm using Qt version 3.3.7 and PyQt version 3.5.5-29 on openSuSE 10.2, if that's relevant.
import sys
from qt import *
# The numbers 1 to 1000 as a string.
NUMBERS = ("%d " * 1000) % (tuple(range(1,1001)))
# Add a textarea containing the numbers 1 to 1000 to the given
# QWidget.
def addTextArea(parent, size):
textbox = QTextEdit(parent)
textbox.setReadOnly(True)
textbox.setMinimumSize(QSize(size, size*0.75))
textbox.setText(NUMBERS)
class TestDialog(QDialog):
def __init__(self,parent=None):
QDialog.__init__(self,parent)
self.setCaption("Dialog")
everything = QVBox(self)
addTextArea(everything, 400)
everything.resize(everything.sizeHint())
class TestMainWindow(QMainWindow):
def __init__(self,parent=None):
QMainWindow.__init__(self,parent)
self.setCaption("Main Window")
everything = QVBox(self)
addTextArea(everything, 800)
button = QPushButton("Open dialog", everything)
self.connect(button, SIGNAL('clicked()'), self.openDialog)
self.setCentralWidget(everything)
self.resize(self.sizeHint())
self.dialog = TestDialog(self)
def openDialog(self):
self.dialog.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
mainwin = TestMainWindow(None)
app.setMainWidget(mainwin)
mainwin.show()
app.exec_loop()
QMainWindow has special behavior for the central widget that a QDialog does not. To achieve the desired behavior you need to create a layout, add the text area to the layout and assign the layout to the dialog.
Just to add a little note about this - I was trying to have a child window spawned from an application, which is a QDialog, containing a single QTextEdit as a child/content - and I wanted the QTextEdit to resize automatically whenever the QDialog window size changes. This seems to have done the trick for me with PyQt4:
def showTextWindow(self):
#QVBox, QHBox # don't exist in Qt4
dialog = QDialog(self)
#dialog.setGeometry(QRect(100, 100, 400, 200))
dialog.setWindowTitle("Title")
dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
textbox = QTextEdit(dialog)
textbox.setReadOnly(True)
textbox.setMinimumSize(QSize(400, 400*0.75))
textbox.setText("AHAAA!")
# this seems enough to have the QTextEdit
# autoresize to window size changes of dialog!
layout = QHBoxLayout(dialog)
layout.addWidget(textbox)
dialog.setLayout(layout)
dialog.exec_()
I had looked at using a QLayout before but had no luck. I was trying to do something like
dialog.setLayout(some_layout)
but I couldn't get that approach to work so I gave up.
My mistake was that I was trying to pass the layout to the dialog when I should have been passing the dialog to the layout.
Adding the lines
layout = QVBoxLayout(self)
layout.add(everything)
to the end of TestDialog.__init__ fixes the problem.
Thanks to Monjardin for prompting me to reconsider layouts.
Check out Python QT Automatic Widget Resizer It's suppose to work well.

Categories