Positioning widgets to QBoxLayout - python

I'm using PySide to build a GUI
I have a QBoxLayout that I have add some Widgets to, the problem is I want control their positions which I was not able to do, I tried what have been provided in the documentation page which is
addWidget( widg, int streatch, int alignment)
but it is not giving me what I want so the final look that I want is something like this
------------------------------------------------------------ ^^ **
&&&&&&
if the line ____ represents the whole window/ layout
I would like the widget which I named wid in my code to be like the dashed line --------
and the sliders to be in the same place as ^^
and finally the button to be in &&&&&
My second question is I want to add some labels to the sliders and to the como-box and I would like to determine the position how can I do that
here is my code
self.wid = GLWidget()
mainLayout = QtGui.QHBoxLayout()
mainLayout.addWidget(self.wid)
self.xSlider = self.createSlider()
self.ySlider = self.createSlider()
mainLayout.addWidget(self.xSlider)
mainLayout.addWidget(self.ySlider)
self.btn = QtGui.QPushButton('OK')
self.btn.resize(self.btn.sizeHint())
mainLayout.addWidget(self.btn)
self.combo = QtGui.QComboBox()
mainLayout.addWidget(self.combo)
self.setLayout(mainLayout)
self.setWindowTitle(self.tr("Hello GL"))
self.setGeometry(350, 350, 1000, 1000)

You could use a QGridLayout instead of QHBoxLayout and use QGridLayout's setColumnMinimumWidth method to dictate your required widget width.Like that:
mainLayout = QtGui.QGridLayout()
mainLayout.setColumnMinimumWidth(0, 100) #set column 0 width to 100 pixels
button = QtGui.QPushButton('OK')
mainLayout.addWidget(button, 0, 0) #adds the button to the widened column
and then continue to add the rest of the widgets.

Related

Multiple widgets in one item [duplicate]

I have a situation where i want to add 3 buttons in a QTableWidget.
I could able to add a single button using below code.
self.tableWidget = QtGui.QTableWidget()
saveButtonItem = QtGui.QPushButton('Save')
self.tableWidget.setCellWidget(0,4,saveButtonItem)
But i want to know how to add multiple (lets say 3) buttons. I Mean Along with Save Button i want to add other 2 buttons like Edit, Delete in the same column (Actions)
You can simply create your own widget, containing the three buttons, e.g. via subclassing QWidget:
class EditButtonsWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(EditButtonsWidget,self).__init__(parent)
# add your buttons
layout = QtGui.QHBoxLayout()
# adjust spacings to your needs
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
# add your buttons
layout.addWidget(QtGui.QPushButton('Save'))
layout.addWidget(QtGui.QPushButton('Edit'))
layout.addWidget(QtGui.QPushButton('Delete'))
self.setLayout(layout)
And then, set this widget as the cellwidget:
self.tableWidget.setCellWidget(0,4, EditButtonsWidget())
You use a layout widget to add your widgets to, then add the layout widget to the cell.
There are a couple of different ones you can use.
http://doc.qt.io/qt-4.8/layout.html
self.tableWidget = QtGui.QTableWidget()
layout = QtGui.QHBoxLayout()
saveButtonItem = QtGui.QPushButton('Save')
editButtonItem = QtGui.QPushButton('Edit')
layout.addWidget(saveButtonItem)
layout.addWidget(editButtonItem)
cellWidget = QtGui.QWidget()
cellWidget.setLayout(layout)
self.tableWidget.setCellWidget(0, 4, cellWidget)

How to prevent window and widgets in a pyqt5 application from changing size when the visibility of one widget is altered

I want to create a dialog, in which the user should first select one item in a drop down, and for some choices specify an additional parameter. For the sake of the example let's say that the possible choices are A and B and for B the user has to enter a text. The text field should not be visible when A is selected.
Here is a MWE:
#!/usr/bin/env python
import sys
from PyQt5.QtWidgets import QApplication, QComboBox, QDialog, QGridLayout, QLineEdit
class Example(QDialog) :
def __init__(self, parent=None) :
super(QDialog, self).__init__(parent)
self.mainLayout = QGridLayout()
self.setLayout(self.mainLayout)
self.comboBox = QComboBox()
self.comboBox.addItems(['A', 'B'])
self.mainLayout.addWidget(self.comboBox, 0, 0)
self.lineEdit = QLineEdit('')
self.lineEdit.setMinimumWidth(50)
self.mainLayout.addWidget(self.lineEdit, 0, 1)
self.comboBox.activated[str].connect(self.update)
self.update(str(self.comboBox.currentText()))
def update(self, choice) :
if 'B' in choice :
self.lineEdit.setVisible(True)
else :
self.lineEdit.setVisible(False)
if __name__ == '__main__':
app = QApplication(sys.argv)
example = Example()
example.show()
sys.exit(app.exec_())
The problem is, that when initially choice A is presented, the size of the dialog is just enough for the comboBox. When option B is selected, the window is expanded and everything is as it should be. However, when option A is selected again, the comboBox' width increases, taking up all of the avalaible space, instead of leaving empty space to the right.
How can I have space allocated for the text field, no matter if visible or not? What am I missing here?
EDIT The answer by S.Nick solves the problem of the MWE in a way, but not the way I was hoping for: As soon as the scenario is more complex, widgets get reallocated again, e.g. if a QLabel is added in front of the comboBox
self.label = QLabel('label')
self.mainLayout.addWidget(self.label, 0, 0)
self.comboBox = QComboBox()
self.comboBox.addItems(['A', 'B'])
self.mainLayout.addWidget(self.comboBox, 0, 1, alignment=Qt.AlignLeft)
self.lineEdit = QLineEdit('', self)
self.lineEdit.setMinimumWidth(50)
self.mainLayout.addWidget(self.lineEdit, 0, 2)
then the comboBox is flipped around when changing the selection. What I want is that, once in the beginning space and position is allocated for each widget and that the space and position is permanent no matter if any widget is visible or not.
You could try something like this:
def __init__(self, parent=None) :
super(QDialog, self).__init__(parent)
self.mainLayout = QGridLayout()
self.setLayout(self.mainLayout)
self.label = QLabel('label')
self.mainLayout.addWidget(self.label, 0, 0)
self.comboBox = QComboBox()
self.comboBox.addItems(['A', 'B'])
self.mainLayout.addWidget(self.comboBox, 0, 1)
self.lineEdit = QLineEdit('', self)
self.lineEdit.setMinimumWidth(200)
self.mainLayout.addWidget(self.lineEdit, 0, 2)
self.comboBox.activated[str].connect(self.update)
self.mainLayout.setColumnStretch(2,1)
self.adjustSize()
self.update(str(self.comboBox.currentText()))
self.mainLayout.setColumnStretch(2,1) will make sure that the last column will take up all the extra horizontal space even when the line edit widget is hidden.
self.adjustSize() adjusts the size of the main window to the sum of the sizes of all its child widgets. Since at this point the line edit widget is still visible, its size is taken into account as well when the size of the main window is adjusted.
Screenshots
Initial window:
After selecting B:

PyQt5 Progress bar to fill the whole QGroupBox?

I am trying to get a progress bar to fill the whole width of a QGroupBox.
So far it looks like:
I am trying to get it to go all the way across. Here is the code:
def progress(self):
gBox = QGroupBox('Progress')
progress_bar = QProgressBar(gBox)
progress_bar.setRange(0, 1)
# progress_bar.setGeometry(30, 40, 200, 25)
hbox = QHBoxLayout()
hbox.addWidget(progress_bar)
hbox.addStretch(1)
gBox.setLayout(hbox)
return gBox
Do I need to stretch the QGroupBox or the QHBoxLayout?
According to the docs:
void QBoxLayout::addStretch(int stretch = 0)
Adds a stretchable space (a QSpacerItem) with zero minimum size and
stretch factor stretch to the end of this box layout.
That is, a spacer is added, that spacer is added to the end, so it pushes the widget to take the size of sizeHint() compressing it.
In your case you do not need it, so remove it.
def progress(self):
gBox = QGroupBox('Progress')
progress_bar = QProgressBar(gBox)
progress_bar.setRange(0, 1)
hbox = QHBoxLayout()
hbox.addWidget(progress_bar)
gBox.setLayout(hbox)
return gBox

PyQt Image(pixmap) gets cropped when other content changes width in a widget

I'm making a table-like widget that displays an image, the file name, and two box-selection areas. I have two objects 'grid_row' & 'grid_table' (both using QGridLayout), grid_row being a single row and grid_table containing x number of grid_rows (I'm designing it like this because it's simply easier to keep track of my custom properties).
The tool looks like this
The final layout is a QVBoxLayout, then from top to bottom, I have QHBoxLayout(the one with a label and combobox), grid_row(for the headers 1,2,3), a scroll_area that contains the grid_table with each one being grid_rows. Lastly another QHBoxLayout for the buttons.
Each grid_row contains a 'image-widget', and two region labels(QLabel). The image widget contains a label(I used setPixmap for display) and a pushbutton. Here are my grid_row and image_widget classes:
class grid_row(QWidget):
def __init__(self, parent=None):
super().__init__()
#self.frame = frame_main()
self.grid_layout = QGridLayout()
self.grid_layout.setSpacing(50)
self.image_widget = image_widget()
self.grid_layout.addWidget(self.image_widget, 0, 0, 1, 1, Qt.AlignHCenter)
self.region_2 = QLabel('null')
self.grid_layout.addWidget(self.region_2, 0, 2, 1, 1, Qt.AlignHCenter)
self.setLayout(self.grid_layout)
self.region_1 = QLabel('null')
self.grid_layout.addWidget(self.region_1, 0, 1, 1, 1, Qt.AlignHCenter)
class image_widget(QWidget):
def __init__(self, parent=None):
super().__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
self.image_widget_layout = QHBoxLayout()
self.image_widget_label = QLabel()
self.image_widget_label.setPixmap(QPixmap('default.png').scaled(96, 54))
self.image_widget_layout.addWidget(self.image_widget_label)
self.img_btn = QPushButton()
self.img_btn.setEnabled(False)
self.img_btn.setText('Drag Here!')
self.image_widget_layout.addWidget(self.img_btn)
self.setLayout(self.image_widget_layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = QWidget()
layout = QVBoxLayout()
grid_row = grid_row()
layout.addWidget(grid_row)
btn = QPushButton('press')
btn.clicked.connect(lambda: grid_row.region_1.setText('[0,0,1920,1080]'))
layout.addWidget(btn)
widget.setLayout(layout)
scroll_area = QScrollArea()
scroll_area.setWidget(widget)
scroll_area.show()
sys.exit(app.exec_())
So currently, I've implemented events that allow me to drag images into the image_widget and click the push button to modify the two regions that are framed (format: [x1, y1, x2, y2]). The problem is that when I do that(e.g. region values go from 'null' to say '[20,20, 500, 500]', the image gets squished because now the labels are taking up more width.
I realize that some size policy needs to be set (and maybe other properties) but I don't know which property to use and on which widget. I want the image to remain the same. Maybe stretch out the width of each column for the grid_row?
To clarify, I want the label containing the pixmap to remain the same size (always 96*54) and fully displayed(not cropped or stretched) at all times.
I've provided the a simplified executable code to display my problem, the classes are the same as my code, I just only put grid_row inside the scroll_area and added a button to change one of the values of the region to simulate the situation. Can provide additional code if needed. Thanks in advance!
Wow sometimes the answer is really one extra line of code...
So the documentation mentions that QScrollArea by default honors the size of its widget. Which is why when I changed the region (to a value that's wider/ more text) the widget does not auto adjust.
I needed to add
scroll_area.setWidgetResizable(True)
to allow the widget to resize wider thus prompting the scroll bars to appear. This way my pixmap image doesn't get cropped from not having enough space.
The easiest way would be to add size constraints to the label before adding to the layout
self.image_widget_label.adjustSize()
self.image_widget_label.setFixedSize(self.image_widget_label.size())
self.image_widget_layout.addWidget(self.image_widget_label)
adjustSize would resize the label depending on the contents.
The more difficult way is to answer the questions :
"when I change the size of the overall window, how do I want this
particular item to behave? When the window is at its minimal size,
which items do I want hidden or out of view? When the window is full
size, where do I want empty spots?"
To answer these better read a bit on Qt Layout management

How to reset the column span of a widget in a QGridLayout?

Is possible to set the column span of a QLineEdit box after it has been added to the layout? I have two QLineEdit boxes in a QGridLayout that are right next to each other horizontally. During the execution of some of my code, one of these boxes gets hidden. I would like to increase the column span where the hidden one was to avoid a weird gap at the end, and reduce it when needed.
I couldn't really find anything in the Qt documentation for this type of change beyond making the adjustment prior to adding the widget to the layout.
There no method for resetting the row- or column-span after a widget has been added. However, addWidget can be called again on the same widget to achieve the same affect, because re-adding a widget to the same layout always implicitly removes it first. So something like this should work:
index = layout.indexOf(widget)
row, column = layout.getItemPosition(index)[:2]
layout.addWidget(widget, row, column, rowspan, colspan)
Here is a simple demo script:
import sys
from PyQt5 import QtCore, QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtWidgets.QPushButton('Toggle Edit')
self.button.clicked.connect(self.handleButton)
self.edit1 = QtWidgets.QLineEdit()
self.edit1.setPlaceholderText('One')
self.edit2 = QtWidgets.QLineEdit()
self.edit2.setPlaceholderText('Two')
layout = QtWidgets.QGridLayout(self)
layout.addWidget(self.edit1, 0, 0)
layout.addWidget(self.edit2, 0, 1)
layout.addWidget(self.button, 1, 0)
def handleButton(self):
if self.edit2.isHidden():
self.setWidgetSpan(self.edit1, 1, 1)
self.edit2.setHidden(False)
else:
self.edit2.setHidden(True)
self.setWidgetSpan(self.edit1, 1, 2)
def setWidgetSpan(self, widget, rowspan=1, colspan=1):
layout = self.layout()
index = layout.indexOf(widget)
row, column = layout.getItemPosition(index)[:2]
layout.addWidget(widget, row, column, rowspan, colspan)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 100)
window.show()
sys.exit(app.exec_())
Try as said below.
When you add two QLineEdit, The QGridLayout dynamically creates two columns.
If you want extra columns for spanning use QSpacerItem and get extra columns.
And you have to use addItem(..,..,..) of QGridLayout to add the spacer item to layout.
Between your two QLineEdit add spacers as shown below
spacer = QtGui.QSpacerItem(20, 20)
layout.addItem(spacer,0,1)
Now if you add totak 4 spacers (for example) ,
spacer1 = QtGui.QSpacerItem(20, 20)
layout.addItem(spacer1,0,2)
spacer2 = QtGui.QSpacerItem(20, 20)
layout.addItem(spacer2,0,3)
spacer3 = QtGui.QSpacerItem(20, 20)
layout.addItem(spacer3,0,4)
Now you have total 6 columns ------- with first QLineEdit (column 0), 4 spacers, last QLineEdit (column 5)
Now you can use setColumnStretch(column,stretch) to set the span of any line edit. Here I am trying to set span for last QLineEdit..as shown below. Spanned for 3 columns...
layout.setColumnStretch(5,3)
Hope this helps...

Categories

Resources