PyQt: How to stick a widget to the bottom edge of dialog - python

Running this code creates a simple dialog with a label, lineedit and two buttons.
All the widgets beautifully respond to the dialog horizontal resizing. But the buttons at the bottom of the dialog do not stick to the lower edge of dialog window when it is being resized vertically. What would be a possible solution to make sure the buttons are always positioned at the bottom edge of dialog?
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication(sys.argv)
class mainWindow(QtGui.QMainWindow):
def __init__(self):
super(mainWindow, self).__init__()
mainQWidget = QtGui.QWidget()
mainLayout=QtGui.QFormLayout()
mainLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow)
label = QtGui.QLabel('My Label')
lineEdit = QtGui.QLineEdit()
mainLayout.addRow(label, lineEdit)
ButtonBox = QtGui.QGroupBox()
ButtonsLayout = QtGui.QHBoxLayout()
Button_01 = QtGui.QPushButton("Close")
Button_02 = QtGui.QPushButton("Execute")
ButtonsLayout.addWidget(Button_01)
ButtonsLayout.addWidget(Button_02)
ButtonBox.setLayout(ButtonsLayout)
mainLayout.addRow(ButtonBox)
mainQWidget.setLayout(mainLayout)
self.setCentralWidget(mainQWidget)
if __name__ == '__main__':
window = mainWindow()
window.show()
window.raise_()
window.resize(480,320)
app.exec_()

I would suggest using a QVBoxLayout as your main layout, with a stretch between the QFormLayout and the button's QHBoxLayout.
As an example based on your current dialog:
import sys
from PyQt4 import QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
label = QtGui.QLabel('My Label')
line_edit = QtGui.QLineEdit()
form_layout = QtGui.QFormLayout()
form_layout.addRow(label, line_edit)
close_button = QtGui.QPushButton('Close')
execute_button = QtGui.QPushButton('Execute')
button_layout = QtGui.QHBoxLayout()
button_layout.addWidget(close_button)
button_layout.addWidget(execute_button)
main_layout = QtGui.QVBoxLayout()
main_layout.addLayout(form_layout)
main_layout.addStretch()
main_layout.addLayout(button_layout)
central_widget = QtGui.QWidget()
central_widget.setLayout(main_layout)
self.setCentralWidget(central_widget)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.resize(480, 320)
window.show()
sys.exit(app.exec_())

Related

QScrollArea.ensureWidgetVisible method does not show target widget

I am trying to make the last QPushButton visible by using method QScrollArea().ensureWidgetVisible(), but as you can see this method doesn't scroll till the last QPushButton.
Example
Could you please assist and solve my issue perhaps issue with setFrameStyle? thank you in advance.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Widget(QWidget):
def __init__(self, parent= None):
super(Widget, self).__init__()
self.setFixedHeight(200)
#Container Widget
widget = QWidget()
#Layout of Container Widget
layout = QVBoxLayout(self)
for _ in range(20):
btn = QPushButton("test")
layout.addWidget(btn)
widget.setLayout(layout)
#Scroll Area Properties
scroll = QScrollArea()
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll.setWidgetResizable(False)
scroll.setWidget(widget)
# print(scroll.verticalScrollBar().maximum())
# vbar = scroll.verticalScrollBar()
# vbar.setValue(vbar.maximum())
#vbar.setValue(vbar.maximum())
#Scroll Area Layer add
vLayout = QVBoxLayout(self)
vLayout.addWidget(scroll)
self.setLayout(vLayout)
# items = (layout.itemAt(i) for i in range(layout.count()))
# for w in items:
# print(w)
print(layout.count())
#scroll.ensureWidgetVisible(layout.itemAt(layout.count()-5).widget(), xMargin=10, yMargin=10 )
scroll.ensureWidgetVisible(layout.itemAt(layout.count()-1).widget() )
print(layout.itemAt(layout.count()-1).widget(),"last widget")
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = Widget()
dialog.show()
app.exec_()
The problem is that for efficiency reasons widgets sizes are not calculated or updated until they are displayed, in your case the viewport of QScrollArea has not updated its size and therefore moves the scroll to an intermediate position. A possible solution is to use QTimer::singleShot() to call the function ensureWidgetVisible() a moment after it has been displayed:
import sys
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent= None):
super(Widget, self).__init__(parent)
self.setFixedHeight(200)
#Container Widget
widget =QtWidgets.QWidget()
#Layout of Container Widget
layout = QtWidgets.QVBoxLayout(widget)
for _ in range(20):
btn = QtWidgets.QPushButton("test")
layout.addWidget(btn)
scroll = QtWidgets.QScrollArea()
scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
scroll.setWidgetResizable(False)
scroll.setWidget(widget)
#Scroll Area Layer add
vLayout = QtWidgets.QVBoxLayout(self)
vLayout.addWidget(scroll)
last_widget = layout.itemAt(layout.count()-1).widget()
QtCore.QTimer.singleShot(0, partial(scroll.ensureWidgetVisible, last_widget))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialog = Widget()
dialog.show()
sys.exit(app.exec_())
or simply call show() before:
...
last_widget = layout.itemAt(layout.count()-1).widget()
self.show()
scroll.ensureWidgetVisible(last_widget)

PyQt5 QScrollArea widget with dynamically created GroupBoxes

I'm trying to make a toolbox widget that will do various different things. But I'm having trouble with the layout management regarding the QScrollArea. Following the stripped version of the code I have:
from PyQt5 import QtWidgets
import sys
class MyScrollWidget(QtWidgets.QWidget):
def __init__(self):
super(MyScrollWidget, self).__init__()
scrollArea = QtWidgets.QScrollArea(self)
top_widget = QtWidgets.QWidget()
top_layout = QtWidgets.QVBoxLayout()
for i in range(10):
group_box = QtWidgets.QGroupBox()
group_box.setTitle('GroupBox For Item {0}'.format(i))
layout = QtWidgets.QHBoxLayout(group_box)
label = QtWidgets.QLabel()
label.setText('Label For Item {0}'.format(i))
layout.addWidget(label)
push_button = QtWidgets.QPushButton(group_box)
push_button.setText('Run Button')
push_button.setFixedSize(100, 32)
layout.addWidget(push_button)
group_box.setLayout(layout)
top_layout.addWidget(group_box)
top_widget.setLayout(top_layout)
scrollArea.setWidget(top_widget)
self.resize(200, 500)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
widget = MyScrollWidget()
widget.show()
sys.exit(app.exec_())
But this only gives me a small fixed subsection of the widget that scrolled. But what I really wants is the whole widget to be scrollable if the widget/window is smaller than the total size of all the group boxes. I.e I'd like the widget to be used as if it was all fixed width, but if the user resized the widget smaller than that, it would scroll appropriately. I've tried various different methods with no good results so now I'm deferring to those who have more experience with layout management than I. Thank you for your time.
You have to set the scrollArea to MyScrollWidget using a layout.
from PyQt5 import QtWidgets
import sys
class MyScrollWidget(QtWidgets.QWidget):
def __init__(self):
super(MyScrollWidget, self).__init__()
lay = QtWidgets.QVBoxLayout(self)
scrollArea = QtWidgets.QScrollArea()
lay.addWidget(scrollArea)
top_widget = QtWidgets.QWidget()
top_layout = QtWidgets.QVBoxLayout()
for i in range(10):
group_box = QtWidgets.QGroupBox()
group_box.setTitle('GroupBox For Item {0}'.format(i))
layout = QtWidgets.QHBoxLayout(group_box)
label = QtWidgets.QLabel()
label.setText('Label For Item {0}'.format(i))
layout.addWidget(label)
push_button = QtWidgets.QPushButton(group_box)
push_button.setText('Run Button')
push_button.setFixedSize(100, 32)
layout.addWidget(push_button)
top_layout.addWidget(group_box)
top_widget.setLayout(top_layout)
scrollArea.setWidget(top_widget)
self.resize(200, 500)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
widget = MyScrollWidget()
widget.show()
sys.exit(app.exec_())

PyQt: How to create a scrollable window

I think it should be much easier to create a scrollable window in PyQt.
I have a list of labels that goes out of the window and I would like to scroll down to view them. At the moment the code does not give me an error, but the window just doesn't appear:
class Example(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
lbl_arr = makeLabelArr()
for i in range(1,8):
qb = lbl_arr[i]
# qb.setFixedWidth(300)
layout.addWidget(qb)
layout.setAlignment(Qt.AlignTop)
scroll = QScrollArea()
scroll.setWidget(self)
scroll.setWidgetResizable(True)
scroll.setFixedHeight(400)
layout.addWidget(scroll)
self.setLayout(layout)
self.setGeometry(0, 0, 600, 220)
self.setWindowTitle('SnP watchlist')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
#print(QDesktopWidget().availableGeometry())
ex = Example()
sys.exit(app.exec_())
Make the window itself a QScrollArea, like this:
class Window(QScrollArea):
def __init__(self):
super(Window, self).__init__()
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setAlignment(Qt.AlignTop)
for index in range(100):
layout.addWidget(QLabel('Label %02d' % index))
self.setWidget(widget)
self.setWidgetResizable(True)
There is an example here: https://www.learnpyqt.com/tutorials/qscrollarea/
from PyQt5.QtWidgets import (QWidget, QSlider, QLineEdit, QLabel, QPushButton, QScrollArea,QApplication,
QHBoxLayout, QVBoxLayout, QMainWindow)
from PyQt5.QtCore import Qt, QSize
from PyQt5 import QtWidgets, uic
import sys
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.scroll = QScrollArea() # Scroll Area which contains the widgets, set as the centralWidget
self.widget = QWidget() # Widget that contains the collection of Vertical Box
self.vbox = QVBoxLayout() # The Vertical Box that contains the Horizontal Boxes of labels and buttons
for i in range(1,50):
object = QLabel("TextLabel: "+str(i))
self.vbox.addWidget(object)
self.widget.setLayout(self.vbox)
#Scroll Area Properties
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.widget)
self.setCentralWidget(self.scroll)
self.setGeometry(600, 100, 1000, 900)
self.setWindowTitle('Scroll Area Demonstration')
self.show()
return
def main():
app = QtWidgets.QApplication(sys.argv)
main = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You should set layout after adding the scroll bar widget.
class Example(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
lbl_arr = makeArrayOfLabelsHTML()
for i in range(1,8):
qb = lbl_arr[i]
layout.addWidget(qb)
layout.setAlignment(Qt.AlignTop)
scroll = QScrollArea()
scroll.setWidget(self)
scroll.setWidgetResizable(True)
scroll.setFixedHeight(400)
layout.addWidget(scroll)
# set layout after adding scroll bar
self.setLayout(layout)
self.setGeometry(0, 0, 600, 220)
self.setWindowTitle('SnP watchlist')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
#print(QDesktopWidget().availableGeometry())
ex = Example()
sys.exit(app.exec_())

How to focus view on QLabel

I want to find QLabel with specific text and focus view on it. Finding widget with desired text is easy, but I couldn't figure out how to focus view on it.
The code so far looks like this:
import sys
from PySide import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super().__init__()
widget = QtGui.QWidget()
self.layout = QtGui.QGridLayout()
for i in range(10):
label = QtGui.QLabel("aaaa" + str(i))
self.layout.addWidget(label, i, 0)
widget.setLayout(self.layout)
self.toolbar = self.addToolBar("aa")
findAction = QtGui.QAction('Find', self)
findAction.triggered.connect(self.find)
self.toolbar.addAction(findAction)
self.scroll = QtGui.QScrollArea()
self.scroll.setWidget(widget)
self.scroll.setWidgetResizable(True)
self.setMaximumSize(200, 200)
self.setCentralWidget(self.scroll)
def find(self):
widgets = (self.layout.itemAt(i).widget() for i in range(self.layout.count()))
for w in widgets:
if isinstance(w, QtGui.QLabel):
if w.text() == "aaaa9":
w.setFocus()
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec_()
if __name__ == "__main__":
main()
Any ideas how to get focus working?
You can use ensureWidgetVisible()
self.scroll.ensureWidgetVisible(w)

Toolbar and BoxLayout overlay

This is my code:
import sys
from PyQt4 import QtGui, QtCore
def prova():
print "test event"
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(350, 250) # ridimensiona la finestra
self.setWindowTitle('MainWindow')
pause = QtGui.QAction(QtGui.QIcon("icons/pause.gif"),"Pause",self)
pause.setStatusTip("Pause!!")
pause.triggered.connect(prova);
play = QtGui.QAction(QtGui.QIcon("icons/play.png"),"Play",self)
play.setStatusTip("Start!")
toolbar = self.addToolBar('My toolbar')
toolbar.addAction(pause)
toolbar.addAction(play)
toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
widget = QtGui.QWidget(self)
hbox = QtGui.QHBoxLayout(widget)
label = QtGui.QLabel()
label.setText("test label")
hbox.addWidget(label)
hbox.setAlignment(label,QtCore.Qt.Alignment(QtCore.Qt.AlignRight))
app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
My problem is that the horizontal box hbox and the widget inside, are overlayed on the toolbar. How do i fix the overlay, putting the hbox below the toolbar?
The problem is that you're adding widget directly as child of main instead of setting it as central widget. So instead of:
widget = QtGui.QWidget(self)
use:
widget = QtGui.QWidget()
self.setCentralWidget(widget)

Categories

Resources