Layout management

A layout can be defined as the sizing, spacing, and placement of content within a GUI window. It defines the visual structure of a user interface. Effective layout management will please users by assisting them in quickly locating what the users want most from the application, and also helps in making out the differences between a good informative design and confusing, puzzling designs for the application. Therefore, management of layout in a window-style application is a crucial success factor for any GUI application. A good layout must have a priority focus, smooth flow, logical grouping, relative emphasis, and coordinated alignment.

In PySide, we use two approaches to layout management. They are as follows:

  • Absolute positioning: This is a crude way of setting the layout manually for each widget by giving them their position and size
  • Layout containers: This is a way to handle the automatic positioning and resizing of widgets by the layout management classes used in Qt

Absolute positioning

Absolute positioning is the most crude and naive form of arranging widgets in the window. This is achieved by giving a hard-wired position and size to all the widgets in the window. Usually, we use the widget's move(x, y) function where x and y are the horizontal and vertical distance, respectively, from the top left corner of the form to the top left corner of the widget. We have already seen this method of layout positioning in our previous chapters when positioning the About and Quit buttons.

This method of layout management is highly discouraged and not effective due to the following disadvantages:

  • Calculating the size and position for each widget manually is a really cumbersome task
  • Resizing the window changes the layout
  • It does not respond to style changes
  • Internationalization and font changes become very difficult as the label text may overflow or underflow
  • It may appear completely different in different resolutions

Therefore, it is highly advised to use the layout containers for layout management. We have already seen examples for this type of layout management.

Layout containers

All widgets can use layouts to manage their children. If these are used in a proper way, the following functions are achieved:

  • Sensible default size for windows
  • Positioning of child widgets
  • Resize handling
  • Automatic updates when content changes, including font size, hiding, removal of widgets, and so on

The container class overcomes all the disadvantages that were discussed in absolute positioning and is the most widely used layout management system. Containers are more flexible and adjust the layout in accordance with the style of the different platforms. We will look into the commonly used layout containers inherited from the PySide.QtGui.QLayout class:

  • QBoxLayout: This lines up widgets horizontally or vertically, as follows:
    • QHBoxLayout: This lines up widgets horizontally
    • QVBoxLayout: This lines up widgets vertically
  • QGridLayout: This lays out widgets in a grid
  • QFormLayout: This manages the form of input widgets and their label
  • QStackedLayout: This is a stack of widgets where only one widget is visible at a time

We will discuss each of them in slight detail.

QBoxLayout

The PySide.QtGui.QBoxLayout class takes the space it gets, divides it up into a row of boxes, and makes each managed widget fill one box. The orientation or direction of the box layout can be either vertical (column-wise) or horizontal (row-wise). The direction can take any one of the following values:

  • QBoxLayout.LeftToRight: This takes the horizontal direction from left to right
  • QBoxLayout.RightToLeft: This takes the horizontal direction from right to left
  • QBoxLayout.TopToBottom: This takes the vertical direction from top to bottom
  • QBoxLayout.BottomToTop: This takes the vertical direction from bottom to top

Each of the filled widgets will get its minimum size at the least and its maximum size at the most. The extra space is shared between the widgets according to their stretch factor. The QBoxLayout class can be attached to any of the parent layouts because it is not the top-level layout.

The widgets can be added to the QBoxLayout function using the following four functions:

  • QBoxLayout.addWidget(): This helps add widgets
  • QBoxLayout.addSpacing(): This creates an empty box to add spacing
  • QBoxLayout.addStretch(): This creates an empty stretchable box
  • QBoxLayout.addLayout(): This adds a box with another layout and defines its stretch factor

The add functions can be replaced with insert functions, for example, QBoxLayout.insertWidget() to insert a box at a specified position in the layout.

This class also includes two margins. The Pyside.QtGui.QLayout.setContentsMargins() function sets the width of the outer border on each side of the widget, and PySide.QtGui.QBoxLayout.setSpacing() sets the width between the neighboring boxes. The default margin spaces differ by the application style.

The most convenient and easy way to use the QBoxLayout class is to instantiate one of its inherited classes, QVBoxLayout and QHBoxLayout, for vertical and horizontal direction layout, respectively. The following programs will explain the usage of QVBoxLayout and QHBoxLayout.

QHBoxLayout

An example of the horizontal layout is as follows:

class MainWindow(QWidget):
  """ Our Main Window class
  """
  def __init__(self):
    """ Constructor Function
    """
    super(MainWindow,self).__init__()
    self.initGUI()

  def initGUI(self):
    self.setWindowTitle("Horizontal Layout")
    self.setGeometry(300, 250, 400, 300)
    self.SetLayout()
    self.show()


  def SetLayout(self):
    """ Function to add buttons and set the layout
    """
    horizontalLayout = QHBoxLayout(self)
    hButton1 = QPushButton('Button 1', self)
    hButton2 = QPushButton('Button 2', self)
    hButton3 = QPushButton('Button 3', self)
    hButton4 = QPushButton('Button 4', self)
    horizontalLayout.addWidget(hButton1)
    horizontalLayout.addWidget(hButton2)
    horizontalLayout.addWidget(hButton3)
    horizontalLayout.addWidget(hButton4)
    
    self.setLayout(horizontalLayout)

QVBoxLayout

An example of the vertical layout is as follows:

def SetLayout(self):
    verticalLayout = QVBoxLayout(self)
    vButton1 = QPushButton('Button 1', self)
    vButton2 = QPushButton('Button 2', self)
    vButton3 = QPushButton('Button 3', self)
    vButton4 = QPushButton('Button 4', self)

    verticalLayout.addWidget(vButton1)
    verticalLayout.addWidget(vButton2)
    verticalLayout.addStretch()
    verticalLayout.addWidget(vButton3)
    verticalLayout.addWidget(vButton4)
    
    self.setLayout(verticalLayout)
QVBoxLayout

QGridLayout

The PySide.QtGui.QGridLayout class takes the space that is available to it, divides it up into rows and columns, and puts each widget that it manages into the next available cell. Similar to HTML tables, each row/column has a minimum width and a stretch factor. The widgets are added into the grid layout using the addWidget() function and the layout puts it into the correct cell. It is also possible for a widget to span across multiple rows/columns. The addItem() and addLayout() methods can also be used to insert widgets or other layouts into it.

The grid layout also includes two margins, as discussed in the box layout. An example program for the usage of grid layout is as follows:

def SetLayout(self):
    gridLayout = QGridLayout(self)
    gButton1 = QPushButton('Button 1', self)
    gButton2 = QPushButton('Button 2', self)
    gButton3 = QPushButton('Button 3', self)
    gButton4 = QPushButton('Button 4', self)
    gButton5 = QPushButton('Button 5', self)
    gridLayout.addWidget(gButton1, 0, 0)
    gridLayout.addWidget(gButton2, 0, 1)
    gridLayout.addWidget(gButton3, 1, 0, 1, 2)
    gridLayout.addWidget(gButton4, 2, 0)
    gridLayout.addWidget(gButton5, 2, 1)
  self.setLayout(gridLayout)

QFormLayout

The PySide.QtGui.QFormLayout class is a higher-level alternative to the basic forms of layout classes, which lays out its widgets in a two-column form. Usually, the left column consists of label, and the right column consists of the field widgets, such as line editors, combo box, spin box, and so on.

The form layout can be achieved through the grid layout, but using it directly on the form-like widgets has the following advantages:

  • Adherence to different platforms' look and feel
  • Support to wrap long rows, using RowWrapPolicy control
  • API to create label-field pairs
  • Support to expand fields using FieldGrowthPolicy control

The spacing between the rows of forms can be set using the setHorizontalSpacing() and setVerticalSpacing() functions of this class. An example of the form layout is as follows:

def SetLayout(self):
    formLayout = QFormLayout(self)
    labelUsername = QLabel("Username")
    txtUsername = QLineEdit()
    labelPassword = QLabel("Password")
    txtPassword = QLineEdit()
    formLayout.addRow(labelUsername, txtUsername)
    formLayout.addRow(labelPassword, txtPassword)
    self.setLayout(formLayout)

QStackedLayout

The PySide.QtGui.QStackedLayout class lays out a set of child widgets, and it shows them only one at a time, hiding the others from the user. The layout can be initially populated with a number of child widgets, and later on, any one of them can be selected to be shown to the user depending on the choice in window. There is no intrinsic way given by the layout itself for the users to select a widget from the available child widgets. This is achieved through using either QComboBox or QListWidget widgets. On populating the layout, the child widgets are added to an internal list, and the index is returned by the layout to select child widgets. The widgets can be inserted and removed using the insertWidget() and removeWidget() functions respectively.

The implementation of the stack layout is left as an exercise for you. The sample program can be found in the code samples that come along with this book.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.14.132