Python PyQt Setting Scroll Area - python

I am trying to make my QGroupBox scrollable once it grow higher than 400px. The contents in the QGroupBox are generated using a for loop. This is an example of how it was done.
mygroupbox = QtGui.QGroupBox('this is my groupbox')
myform = QtGui.QFormLayout()
labellist = []
combolist = []
for i in range(val):
labellist.append(QtGui.QLabel('mylabel'))
combolist.append(QtGui.QComboBox())
myform.addRow(labellist[i],combolist[i])
mygroupbox.setLayout(myform)
Since the value of val depends on some other factors, the myform layout size could not be determined. In order to solve this, i added a QScrollableArea like this.
scroll = QtGui.QScrollableArea()
scroll.setWidget(mygroupbox)
scroll.setWidgetResizable(True)
scroll.setFixedHeight(400)
Unfortunately, that doesn't seems to make any effect on the groupbox. No sign of scrollbar. Am i missing somthing?

Other than the obvious typo (I'm sure you meant QScrollArea), I can't see anything wrong with what you've posted. So the problem must lie elsewhere in your code: a missing layout maybe? Just to make sure we're on the same page, the minimal script below works as expected for me:
PyQt5
from PyQt5 import QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self, val):
super().__init__()
mygroupbox = QtWidgets.QGroupBox('this is my groupbox')
myform = QtWidgets.QFormLayout()
labellist = []
combolist = []
for i in range(val):
labellist.append(QtWidgets.QLabel('mylabel'))
combolist.append(QtWidgets.QComboBox())
myform.addRow(labellist[i],combolist[i])
mygroupbox.setLayout(myform)
scroll = QtWidgets.QScrollArea()
scroll.setWidget(mygroupbox)
scroll.setWidgetResizable(True)
scroll.setFixedHeight(200)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(scroll)
if __name__ == '__main__':
app = QtWidgets.QApplication(['Test'])
window = Window(12)
window.setGeometry(500, 300, 300, 200)
window.show()
app.exec_()
PyQt4
from PyQt4 import QtGui
class Window(QtGui.QWidget):
def __init__(self, val):
QtGui.QWidget.__init__(self)
mygroupbox = QtGui.QGroupBox('this is my groupbox')
myform = QtGui.QFormLayout()
labellist = []
combolist = []
for i in range(val):
labellist.append(QtGui.QLabel('mylabel'))
combolist.append(QtGui.QComboBox())
myform.addRow(labellist[i],combolist[i])
mygroupbox.setLayout(myform)
scroll = QtGui.QScrollArea()
scroll.setWidget(mygroupbox)
scroll.setWidgetResizable(True)
scroll.setFixedHeight(200)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(scroll)
if __name__ == '__main__':
app = QtGui.QApplication(['Test'])
window = Window(12)
window.setGeometry(500, 300, 300, 200)
window.show()
app.exec_()

Related

pyqt5 Is there a limit to loading widgets using clicked.connect?

I'm using the QPushButton to load the UI. First -> Jumin -> Department -> next -> next I want to create the UI in order. The problem is that I can not load the third Department into the QMainwindow window. I do not know why
When you create a widget in QVBoxLayout, it changes the size of the widget according to the wallpaper like wxpython layout (wx.all). Can not change the position (move) and size (resize) by automatic centering?
import sys
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.center_widget = QWidget()
self.setCentralWidget(self.center_widget)
self.FirstUI()
def FirstUI(self):
self.btn1 = QPushButton('test1', self)
self.btn1.move(50, 50)
self.btn1.clicked.connect(self.btn1_click)
def JuminUI(self):
self.ju1 = QLineEdit('13')
self.btn2 = QPushButton('^^^^^^^^^^')
self.ju_text = QLabel('asd')
self.jumim_layout = QVBoxLayout()
self.jumim_layout.addWidget(self.ju_text)
self.jumim_layout.addWidget(self.ju1)
self.jumim_layout.addWidget(self.btn2)
self.centralWidget().setLayout(self.jumim_layout)
self.btn2.clicked.connect(self.btn2_click)
def DepartmentUI(self):
self.depart_layout = QVBoxLayout()
self.depart_layout.addWidget(QPushButton('sdfsdf'))
self.centralWidget().setLayout(self.depart_layout)
def btn1_click(self):
self.btn1.deleteLater()
self.JuminUI()
def btn2_click(self):
self.ju1.deleteLater()
self.btn2.deleteLater()
self.ju_text.deleteLater()
self.DepartmentUI()
if __name__ == "__main__":
app = QApplication(sys.argv)
fream = MainWindow()
fream.show()
app.exec_()
creating and removing widgets is almost always a bad idea, and your code falls into those bad ideas, it's always best to hide the widgets and for that you should use the QStackedWidget, what QStackedWidget does is just make a widget visible on all widgets that you have been assigned by changing the currentIndex.
import sys
from functools import partial
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.center_widget = QtWidgets.QStackedWidget()
self.setCentralWidget(self.center_widget)
self.FirstUI()
self.JuminUI()
self.DepartmentUI()
def FirstUI(self):
widget = QtWidgets.QWidget()
self.btn1 = QtWidgets.QPushButton('test1', widget)
self.btn1.move(50, 50)
self.center_widget.addWidget(widget)
self.btn1.clicked.connect(partial(self.center_widget.setCurrentIndex, 1))
def JuminUI(self):
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
self.ju1 = QtWidgets.QLineEdit('13')
self.btn2 = QtWidgets.QPushButton('^^^^^^^^^^')
self.ju_text = QtWidgets.QLabel('asd')
lay.addWidget(self.ju_text)
lay.addWidget(self.ju1)
lay.addWidget(self.btn2)
self.center_widget.addWidget(widget)
self.btn2.clicked.connect(partial(self.center_widget.setCurrentIndex, 2))
def DepartmentUI(self):
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
lay.addWidget(QtWidgets.QPushButton('sdfsdf'))
self.center_widget.addWidget(widget)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
fream = MainWindow()
fream.show()
sys.exit(app.exec_())

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_())

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)

PyQt QVBoxLayout and missing widgets?

I am trying to set up a window that has a text input & a combo box. At the moment I just want to see the text & the selection displayed under the appropriate widget.
I have used QVBoxLayout() as I will be adding more stuff later & thought it would be a simple way of laying out the window.
Unfortunately only the combo box ever gets displayed. The code:
from PyQt4 import QtCore, QtGui
import sys
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
self.initUI()
def initUI(self):
# Poly names
self.pNames = QtGui.QLabel(self)
polyNameInput = QtGui.QLineEdit(self)
# polyName entry
polyNameInput.textChanged[str].connect(self.onChanged)
# Polytype selection
self.defaultPolyType = QtGui.QLabel("Random polyhedra", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
# Layout
vbox = QtGui.QVBoxLayout()
vbox.addWidget(polyNameInput)
vbox.addWidget(self.pNames)
vbox.addWidget(polyType)
vbox.addWidget(self.defaultPolyType)
vbox.addStretch()
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
# Poly names
def onChanged(self, text):
self.pNames.setText(text)
self.pNames.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
So whats going on here? Am I missing some important directive to QVBoxLayout()?
Using Python 2.7 on Win 7 x64 machine with PyQt 4.
EDIT: Additional problem (still related to missing widgets)
I have amended the code following the clarification below. I then added more widgets when a certain option in the combobox is chosen (see below) but these widgets dont show. I attempted to add a child widget to 'widget' called 'ranPolyWidget' to take a numerical input.
# Combo box
def onActivated(self, text):
if text=="Random polyhedra":
self.randomSeedLbl = QtGui.QLabel("Seed: ", self)
randomSeed = QtGui.QLineEdit(self)
randomSeed.textChanged[str].connect(self.setSeed)
ranPolyWidget = QtGui.QWidget(self.widget)
rbox = QtGui.QVBoxLayout(ranPolyWidget)
rbox.addWidget(randomSeed)
self.layout().addWidget(ranPolyWidget)
self.show()
else:
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
Same issue as before, no widgets. I am missing something pretty fundamental arent I?
You're forgetting to set it to the widget or main window, so since the QComboBox is the last one made, it's the only one displayed. Basically, everything is added to the layout, but the layout is "free-floating", and so it does not display properly. You need to bind the layout to a QWidget, which I do here. For most widgets, you can can do this by the QtGui.QVBoxLayout(widget) or by widget.setLayout(layout).
Alternatively, if you want multiple layouts on a widget, you can do have a parent layout and then add each child layout to the main layout.
EDIT: This is a better answer:
Make a widget, set layout to widget and set as central widget.
QMainWindow-s don't like you using the builtin layout or overriding it.
widget = QtGui.QWidget()
vbox = QtGui.QVBoxLayout(widget)
self.setCentralWidget(widget)
Old Answer:
self.layout().addLayout(vbox).
This should fix your issue:
Changes I made:
Since QMainWindow already has a layout, add in a widget (28G) and then set the VBoxLayout to the widget and add it to the main window.
from PyQt4 import QtCore, QtGui
import sys
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
self.initUI()
def initUI(self):
# Poly names
self.pNames = QtGui.QLabel(self)
polyNameInput = QtGui.QLineEdit(self)
# polyName entry
polyNameInput.textChanged[str].connect(self.onChanged)
# Polytype selection
self.defaultPolyType = QtGui.QLabel("Random polyhedra", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
# Layout
widget = QtGui.QWidget()
vbox = QtGui.QVBoxLayout(widget)
vbox.addWidget(polyNameInput)
vbox.addWidget(self.pNames)
vbox.addWidget(polyType)
vbox.addWidget(self.defaultPolyType)
vbox.addStretch()
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.layout().addWidget(widget)
self.show()
# Combo box
def onActivated(self, text):
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
# Poly names
def onChanged(self, text):
self.pNames.setText(text)
self.pNames.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
EDIT:
For adding new widgets, you should add them to the layout of the central widget and parent them to that widget.
Here's how I'd restructure your full code:
from PyQt4 import QtCore, QtGui
import sys
class CentralWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CentralWidget, self).__init__(parent)
# set layouts
self.layout = QtGui.QVBoxLayout(self)
# Poly names
self.pNames = QtGui.QLabel(self)
polyNameInput = QtGui.QLineEdit(self)
# polyName entry
polyNameInput.textChanged[str].connect(self.onChanged)
# Polytype selection
self.defaultPolyType = QtGui.QLabel("Random polyhedra", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
self.layout.addWidget(polyNameInput)
self.layout.addWidget(self.pNames)
self.layout.addWidget(polyType)
self.layout.addWidget(self.defaultPolyType)
self.layout.addStretch()
def onActivated(self, text):
'''Adds randSeed to layout'''
if text=="Random polyhedra":
self.randomSeedLbl = QtGui.QLabel("Seed: ", self)
randomSeed = QtGui.QLineEdit(self)
randomSeed.textChanged[str].connect(self.setSeed)
self.layout.addWidget(randomSeed)
else:
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
# Poly names
def onChanged(self, text):
self.pNames.setText(text)
self.pNames.adjustSize()
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
# I like having class attributes bound in __init__
# Not very Qt of me, but it's more
# so I break everything down into subclasses
# find it more Pythonic and easier to debug: parent->child
# is easy when I need to repaint or delete child widgets
self.central_widget = CentralWidget(self)
self.setCentralWidget(self.central_widget)
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
'''Pass to child'''
self.central_widget.onActivated(text)
def main():
app = QtGui.QApplication(sys.argv)
ex = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

How to resize the main window again after setFixedSize()?

I would like to resize the MainWindow (QMainWindow) after I make some widgets unvisible and vice versa. And I want to block the window resize.
def hideAndShowWidget(self):
self.widgetObject.setVisible(not self.widgetObject.isVisible() )
# change main window size here
# ...
self.setFixedSize(self.width(), self.height())
My problem is, that i can not change the window size after i call setFixedSize() first time. I read here that I must use QWIDGETSIZE_MAX() to remove constraints, but I don't know how can I use it, I get the error:
NameError: name 'QWIDGETSIZE_MAX' is not defined
I think you have the mechanism more or less right. You just have to make sure the height calculation is done correctly (i.e. before the visibility of the widget changes).
The following example works correctly for me (only tested on Linux, though):
from PySide import QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.widgetObject = QtGui.QTextEdit(self)
self.button = QtGui.QPushButton('Hide Widget', self)
self.button.clicked.connect(self.hideAndShowWidget)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.widgetObject)
self.setFixedSize(300, 200)
def hideAndShowWidget(self):
height = self.height()
if self.widgetObject.isVisible():
height -= self.widgetObject.height()
self.widgetObject.setVisible(False)
self.button.setText('Show Widget')
else:
height += self.widgetObject.height()
self.widgetObject.setVisible(True)
self.button.setText('Hide Widget')
self.setFixedSize(self.width(), height)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Use the sizeHint(). It contains the size the widget would like to have. Set the fixed size exactly to the size hint.
Working example:
from PySide import QtGui
class Window(QtGui.QMainWindow):
def __init__(self):
super().__init__()
self.setFixedSize(400, 300)
widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout(widget)
button = QtGui.QPushButton('Toggle visibility')
button.clicked.connect(self.hideAndShowWidget)
layout.addWidget(button)
self.widgetObject = QtGui.QLabel('Test')
layout.addWidget(self.widgetObject)
self.setCentralWidget(widget)
def hideAndShowWidget(self):
self.widgetObject.setVisible(not self.widgetObject.isVisible() )
# change main window size
self.setFixedSize(self.sizeHint())
app = QtGui.QApplication([])
w = Window()
w.show()
app.exec_()

Categories

Resources