Layout management in Jython Swing

In this part of the Jython Swing programming tutorial, we will introduce layout managers.

When we design the GUI of our application, we decide what components we will use and how we will organise those components in the application. To organise our components, we use specialised non visible objects called layout managers. The Swing toolkit has two kind of components: containers and children. The containers group children into suitable layouts. To create layouts, we use layout managers.

Absolute positioning

In most cases, programmers should use layout managers. There are a few situations, where we can use absolute positioning. In absolute positioning, the programmer specifies the position and the size of each widget in pixels. The size and the position of a widget do not change if we resize a window. Applications look different on various platforms, and what looks OK on Linux, might not look OK on Mac OS. Changing fonts in your application might spoil the layout. If you translate your application into another language, you must redo your layout. For all these issues, use the absolute positioning only when you have a reason to do so.

#!/usr/local/bin/jython
# -*- coding: utf-8 -*-

"""
ZetCode Jython Swing tutorial

In this program, we lay out widgets
using absolute positioning

author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""

from java.awt import Color
from javax.swing import ImageIcon
from javax.swing import JFrame
from javax.swing import JPanel
from javax.swing import JLabel



class Example(JFrame):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

    def initUI(self):

        panel = JPanel()
        panel.setLayout(None)
        panel.setBackground(Color(66, 66, 66))
        self.getContentPane().add(panel)

        rot = ImageIcon("rotunda.jpg")
        rotLabel = JLabel(rot)
        rotLabel.setBounds(20, 20, rot.getIconWidth(), rot.getIconHeight())

        min = ImageIcon("mincol.jpg")
        minLabel = JLabel(min)
        minLabel.setBounds(40, 160, min.getIconWidth(), min.getIconHeight())

        bar = ImageIcon("bardejov.jpg")
        barLabel = JLabel(bar)
        barLabel.setBounds(170, 50, bar.getIconWidth(), bar.getIconHeight())


        panel.add(rotLabel)
        panel.add(minLabel)
        panel.add(barLabel)


        self.setTitle("Absolute")
        self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        self.setSize(350, 300)
        self.setLocationRelativeTo(None)
        self.setVisible(True)


if __name__ == '__main__':
    Example()

In this example, we show three images using absolute positioning.

panel.setLayout(None)

Containers in Swing already have a default layout manager. JPanel has a FlowLayout manager as its default layout manager. We use the setLayout() method with a None parameter to remove the default layout manager and use absolute positioning instead.

rot = ImageIcon("rotunda.jpg")
rotLabel = JLabel(rot)
rotLabel.setBounds(20, 20, rot.getIconWidth(), rot.getIconHeight())

We create an ImageIcon object. We put the icon into the JLabel component to display it. Then we use the setBounds() method to position the label on the panel. The first two parameters are the x, y position of the label. The 3th and 4th parameters are the width and the height of the icon.

panel.add(rotLabel)

We add the label to the panel container.

Absolute
Figure: Absolute positioning

Buttons example

In the following example, we will position two buttons in the bottom right corner of the window.

#!/usr/local/bin/jython
# -*- coding: utf-8 -*-

"""
ZetCode Jython Swing tutorial

In this program, use box layouts
to position two buttons in the
bottom right corner of the window

author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""

from java.awt import Dimension

from javax.swing import JButton
from javax.swing import JFrame
from javax.swing import JPanel
from javax.swing import BoxLayout
from javax.swing import Box


class Example(JFrame):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

    def initUI(self):

        basic = JPanel()
        basic.setLayout(BoxLayout(basic, BoxLayout.Y_AXIS))
        self.add(basic)

        basic.add(Box.createVerticalGlue())

        bottom = JPanel()
        bottom.setAlignmentX(1.0)
        bottom.setLayout(BoxLayout(bottom, BoxLayout.X_AXIS))

        okButton = JButton("OK")
        closeButton = JButton("Close")

        bottom.add(okButton)
        bottom.add(Box.createRigidArea(Dimension(5, 0)))
        bottom.add(closeButton)
        bottom.add(Box.createRigidArea(Dimension(15, 0)))

        basic.add(bottom)
        basic.add(Box.createRigidArea(Dimension(0, 15)))

        self.setTitle("Buttons")
        self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        self.setSize(300, 150)
        self.setLocationRelativeTo(None)
        self.setVisible(True)


if __name__ == '__main__':
    Example()

We will create two panels. The basic panel has a vertical box layout. The bottom panel has a horizontal one. We will put a bottom panel into the basic panel. We will right align the bottom panel. The space between the top of the window and the bottom panel is expandable. It is done by the vertical glue.

basic = JPanel()
basic.setLayout(BoxLayout(basic, BoxLayout.Y_AXIS))
...

bottom = JPanel()
...
bottom.setLayout(BoxLayout(bottom, BoxLayout.X_AXIS))

The basic panel has a vertical box layout. The bottom panel has a horizontal box layout.

bottom.setAlignmentX(1.0)

The bottom panel is right aligned.

basic.add(Box.createVerticalGlue())

We create a vertical glue. The glue is vertically expandable white space, which will push the horizontal box with the buttons to the bottom.

okButton = JButton("OK")
closeButton = JButton("Close")

These are the two buttons that will go into the bottom right corner of the window.

bottom.add(okButton)
bottom.add(Box.createRigidArea(Dimension(5, 0)))

We put the OK button into the horizontal box. We put some rigid space next to the button. So that there is some space between the two buttons.

basic.add(Box.createRigidArea(Dimension(0, 15)))

We put some space between the buttons and the border of the window.

Buttons example
Figure: Buttons example

Windows example

The following example creates the windows dialog using the GroupLayout manager. The dialog comes from the JDeveloper application.

The GroupLayout manager divides the creation of the layout into two steps. In one step, we lay out components alongside the horizontal axis. In the second step, we lay out components along the vertical axis. This is an unusual idea within layout managers, but it works well.

There are two types of arrangements: sequential and parallel. In both kinds of layouts we can arrange components sequentially or in parallel. In a horizontal layout, a row of components is called a sequential group. A column of components is called a parallel group. In a vertical layout, a column of components is called a sequential group. And a row of components is called a parallel group. You must understand these definitions right in order to work with the GroupLayout manager.

#!/usr/local/bin/jython
# -*- coding: utf-8 -*-

"""
ZetCode Jython Swing tutorial

This code lays out components
using the GroupLayout manager

author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""

from java.awt import Dimension
from java.awt import Color

from javax.swing import JButton
from javax.swing import SwingConstants
from javax.swing import JFrame
from javax.swing import JLabel
from javax.swing import JTextArea
from javax.swing import BorderFactory
from javax.swing import GroupLayout


class Example(JFrame):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

    def initUI(self):

        layout = GroupLayout(self.getContentPane())
        self.getContentPane().setLayout(layout)
        layout.setAutoCreateGaps(True)
        layout.setAutoCreateContainerGaps(True)

        self.setPreferredSize(Dimension(350, 300))

        windows = JLabel("Windows")
        area = JTextArea()
        area.setEditable(False)
        area.setBorder(BorderFactory.createLineBorder(Color.gray))
        activate = JButton("Activate")
        close = JButton("Close")
        help = JButton("Help")
        ok = JButton("OK")

        layout.setHorizontalGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup()
                .addComponent(windows)
                .addComponent(area)
                .addComponent(help))
            .addGroup(layout.createParallelGroup()
                .addComponent(activate)
                .addComponent(close)
                .addComponent(ok))
        )

        layout.setVerticalGroup(layout.createSequentialGroup()
            .addComponent(windows)
            .addGroup(layout.createParallelGroup()
                .addComponent(area)
                .addGroup(layout.createSequentialGroup()
                    .addComponent(activate)
                    .addComponent(close)))
            .addGroup(layout.createParallelGroup()
                .addComponent(help)
                .addComponent(ok))
        )

        layout.linkSize(SwingConstants.HORIZONTAL, [ok, help, close, activate])

        self.pack()

        self.setTitle("Windows")
        self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        self.setLocationRelativeTo(None)
        self.setVisible(True)


if __name__ == '__main__':
    Example()

In the above example, we see a chained calls of addComponent() methods. This is possible because the addComponent() method returns the group on which it is called. Thanks to this, we do not need local variables to hold the groups. Also note that the code is properly indented for better readability.

layout.setHorizontalGroup(layout.createSequentialGroup()
    .addGroup(layout.createParallelGroup()
        .addComponent(windows)
        .addComponent(area)
        .addComponent(help))
    .addGroup(layout.createParallelGroup()
        .addComponent(activate)
        .addComponent(close)
        .addComponent(ok))
)

In the first step, we have a horizontal layout. It consists of two parallel groups of three components.

layout.setVerticalGroup(layout.createSequentialGroup()
    .addComponent(windows)
    .addGroup(layout.createParallelGroup()
        .addComponent(area)
        .addGroup(layout.createSequentialGroup()
            .addComponent(activate)
            .addComponent(close)))
    .addGroup(layout.createParallelGroup()
        .addComponent(help)
        .addComponent(ok))
)

Vertical layout is a bit more complex. First, we add a single component. Then we add a parallel group of a single component and a sequential group of two components. Finally, we add a parallel group of two components.

layout.linkSize(SwingConstants.HORIZONTAL, [ok, help, close, activate])

This line makes all buttons the same size. We only need to set their width, because their height is already the same by default.

Windows example
Figure: Windows example

Look at the screenshot of the example. Notice that components can be grouped into vertical and horizontal sets of components. For example, the label the area and the Help button component can form a vertical group of components. This is exactly what the GroupLayout managers does. It lays out components by forming vertical and horizontal groups of components.

In this part of the Jython Swing tutorial, we mentioned layout management of widgets.