PyQt QVBoxLayout and missing widgets?

5.1k views Asked by At

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?

1

There are 1 answers

6
Alex Huszagh On BEST ANSWER

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