Painting in Jython Swing

In this part of the Jython Swing programming tutorial we will do some painting.

We use painting to create charts, custom components or create games. To do the painting, we use the painting API provided by the Swing toolkit. The painting is done within the paintComponent() method. In the painting process, we use the Graphics2D object. It is a graphics context that allows an application to draw onto components. It is the fundamental class for rendering 2-dimensional shapes, text and images.

Colours

A colour is an object representing a combination of Red, Green, and Blue (RGB) intensity values. We use the Color class to work with colours in Swing.

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

"""
ZetCode Jython Swing tutorial

This program draws ten
rectangles filled with different
colors.

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

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

class Canvas(JPanel):

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

    def paintComponent(self, g):

        self.drawColorRectangles(g)

    def drawColorRectangles(self, g):

        g.setColor(Color(125, 167, 116))
        g.fillRect(10, 15, 90, 60)

        g.setColor(Color(42, 179, 231))
        g.fillRect(130, 15, 90, 60)

        g.setColor(Color(70, 67, 123))
        g.fillRect(250, 15, 90, 60)

        g.setColor(Color(130, 100, 84))
        g.fillRect(10, 105, 90, 60)

        g.setColor(Color(252, 211, 61))
        g.fillRect(130, 105, 90, 60)

        g.setColor(Color(241, 98, 69))
        g.fillRect(250, 105, 90, 60)

        g.setColor(Color(217, 146, 54))
        g.fillRect(10, 195, 90, 60)

        g.setColor(Color(63, 121, 186))
        g.fillRect(130, 195, 90, 60)

        g.setColor(Color(31, 21, 1))
        g.fillRect(250, 195, 90, 60)
        

class Example(JFrame):

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

        self.initUI()

    def initUI(self):

        self.canvas = Canvas()
        self.getContentPane().add(self.canvas)
        self.setTitle("Colors")
        self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        self.setSize(360, 300)
        self.setLocationRelativeTo(None)
        self.setVisible(True)


if __name__ == '__main__':
    Example()

In the code example, we draw nine rectangles and fill them with different colour values.

def paintComponent(self, g):

Custom painting is done in paintComponent() in most cases. The g parameter is the graphics context. We call the painting operations on this object.

g.setColor(Color(125, 167, 116))

We set the context's current colour to the specified colour. All subsequent graphics operations using this graphics context use this specified colour.

g.fillRect(10, 15, 90, 60)

We fill a rectangle located at x=10, y=15 having width=90 and height=60 with the above specified colour value.

Colors
Figure: Colours

Shapes

The Swing painting API can draw various shapes. The following programming code example will show some of them.

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

"""
ZetCode Jython Swing tutorial

This program draws basic shapes

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

from java.awt import Color
from java.awt import RenderingHints
from java.awt.geom import Ellipse2D
from javax.swing import JFrame
from javax.swing import JPanel

class Canvas(JPanel):

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

    def paintComponent(self, g):

        self.drawShapes(g)

    def drawShapes(self, g):

        g.setColor(Color(150, 150, 150))

        rh = RenderingHints(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON)

        rh.put(RenderingHints.KEY_RENDERING,
               RenderingHints.VALUE_RENDER_QUALITY)

        g.setRenderingHints(rh)

        g.fillRect(20, 20, 50, 50)
        g.fillRect(120, 20, 90, 60)
        g.fillRoundRect(250, 20, 70, 60, 25, 25)

        g.fill(Ellipse2D.Double(10, 100, 80, 100))
        g.fillArc(120, 130, 110, 100, 5, 150)
        g.fillOval(270, 130, 50, 50)
        

class Example(JFrame):

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

        self.initUI()

    def initUI(self):

        self.canvas = Canvas()
        self.getContentPane().add(self.canvas)
        self.setTitle("Shapes")
        self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        self.setSize(350, 250)
        self.setLocationRelativeTo(None)
        self.setVisible(True)

if __name__ == '__main__':
    Example()

In this code example, we draw six different shapes on the window. A square, a rectangle, a rounded rectangle, an ellipse, an arc and an oval. We do not draw outlines of the shapes, but we fill the inner space of the shapes with a gray color.

rh = RenderingHints(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON)

With the rendering hints, we control the quality of the painting. In the above code, we implement antialiasing. With antialiasing, the shapes are more smooth.

g.setColor(Color(150, 150, 150))

We will be painting in some gray colour.

g.fillRect(20, 20, 50, 50)
g.fillRect(120, 20, 90, 60)
g.fillRoundRect(250, 20, 70, 60, 25, 25)

Here we draw a rectangle, a square and a rounded rectangle. The first four parameters in these methods are the x, y coordinates and width and height. The last two parameters for the fillRoundRect() are the horizontal and vertical diameter of the arc at the four corners.

g.fill(Ellipse2D.Double(10, 100, 80, 100))
g.fillArc(120, 130, 110, 100, 5, 150)
g.fillOval(270, 130, 50, 50)

These three lines draw an ellipse, an arc and an oval.

Shapes
Figure: Shapes

Transparent rectangles

Transparency is the quality of being able to see through a material. The easiest way to understand transparency is to imagine a piece of glass or water. Technically, the rays of light can go through the glass and this way we can see objects behind the glass.

In computer graphics, we can achieve transparency effects using alpha compositing. Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency. The composition process uses an alpha channel. (wikipedia.org, answers.com)

"""
ZetCode Jython Swing tutorial

This program draws ten
rectangles with different
levels of transparency.

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

from java.awt import AlphaComposite
from java.awt import Color
from javax.swing import JFrame
from javax.swing import JPanel


class Canvas(JPanel):

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

    def paintComponent(self, g):

        self.drawRectangles(g)

    def drawRectangles(self, g):

        g.setColor(Color.BLUE)

        for i in range(1, 11):
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                                                        i * 0.1))
            g.fillRect(50 * i, 20, 40, 40)


class Example(JFrame):

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

        self.initUI()

    def initUI(self):

        self.canvas = Canvas()
        self.getContentPane().add(self.canvas)
        self.setTitle("Transparent rectangles")
        self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        self.setSize(590, 120)
        self.setLocationRelativeTo(None)
        self.setVisible(True)


if __name__ == '__main__':
    Example()

In the example we will draw ten rectangles with different levels of transparency.

g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, i * 0.1))

The AlphaComposite class implements basic alpha compositing rules.

Transparent rectangles
Figure: Transparent rectangles

Donut Shape

In the following example we create a complex shape by rotating a bunch of ellipses. An affine transform is composed of zero or more linear transformations (rotation, scaling or shear) and translation (shift). The AffineTransform is the class in Swing to perform affine transformations.

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

"""
ZetCode Jython Swing tutorial

In this program, we create a donut
shape.

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

from java.awt import BasicStroke
from java.awt import Color
from java.awt import RenderingHints
from java.awt.geom import AffineTransform
from java.awt.geom import Ellipse2D
from javax.swing import JFrame
from javax.swing import JPanel

import math

class Canvas(JPanel):

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

    def paintComponent(self, g):

        self.drawDonutShape(g)

    def drawDonutShape(self, g):

        rh = RenderingHints(RenderingHints.KEY_ANTIALIASING,
             RenderingHints.VALUE_ANTIALIAS_ON)

        rh.put(RenderingHints.KEY_RENDERING,
               RenderingHints.VALUE_RENDER_QUALITY)

        g.setRenderingHints(rh)

        size = self.getSize()
        w = size.getWidth()
        h = size.getHeight()

        e = Ellipse2D.Double(0, 0, 80, 130)
        g.setStroke(BasicStroke(1))
        g.setColor(Color.gray)

        for deg in range(0, 360, 5):
            at = AffineTransform.getTranslateInstance(w / 2, h / 2)
            at.rotate(math.radians(deg))
            g.draw(at.createTransformedShape(e))


class Example(JFrame):

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

        self.initUI()

    def initUI(self):

        self.canvas = Canvas()
        self.getContentPane().add(self.canvas)
        self.setTitle("Donut")
        self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        self.setSize(350, 320)
        self.setLocationRelativeTo(None)
        self.setVisible(True)


if __name__ == '__main__':
    Example()

In this example, we create a donut. The shape resembles a cookie, hence the name donut. The donut is centered in the window.

size = self.getSize()
w = size.getWidth()
h = size.getHeight()

Here we determine the width and height of the window. We need these values to center the donut shape.

e = Ellipse2D.Double(0, 0, 80, 130)

We create an ellipse shape. We will rotate this ellipse to create the donut shape.

g.setStroke(BasicStroke(1))
g.setColor(Color.gray)

We set the stroke and the color for the outlines of the shapes.

for deg in range(0, 360, 5):

We draw an ellipse object 72 times. Each time, we rotate the ellipse by additional 5 degrees. This will create our donut shape.

at = AffineTransform.getTranslateInstance(w / 2, h / 2)
at.rotate(math.radians(deg))
g.draw(at.createTransformedShape(e))

With the help of the AffineTransform class we translate the drawing to the center of the window. Then we do rotation. The createTransformedShape() method will apply these affine transforms to the ellipse. And the transformed ellipse is drawn using the draw() method.

Drawing text

In the last example, we are going to draw text on the window.

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

"""
ZetCode Jython Swing tutorial

This program draws lyrics of a
song on the window.

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

from java.awt import Font
from java.awt import RenderingHints
from javax.swing import JFrame
from javax.swing import JPanel

class Canvas(JPanel):

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

    def paintComponent(self, g):

        self.drawLyrics(g)

    def drawLyrics(self, g):

        rh = RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
            RenderingHints.VALUE_TEXT_ANTIALIAS_ON)

        g.setRenderingHints(rh)

        g.setFont(Font("Purisa", Font.PLAIN, 13))

        g.drawString("Most relationships seem so transitory", 20, 30)
        g.drawString("They're all good but not the permanent one", 20, 60)
        g.drawString("Who doesn't long for someone to hold", 20, 90)
        g.drawString("Who knows how to love you without being told", 20, 120)
        g.drawString("Somebody tell me why I'm on my own", 20, 150)
        g.drawString("If there's a soulmate for everyone", 20, 180)
        

class Example(JFrame):

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

        self.initUI()

    def initUI(self):

        self.canvas = Canvas()
        self.getContentPane().add(self.canvas)
        self.setTitle("Soulmate")
        self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
        self.setSize(400, 250)
        self.setLocationRelativeTo(None)
        self.setVisible(True)


if __name__ == '__main__':
    Example()

We draw a lyrics of a song on the window.

rh = RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
    RenderingHints.VALUE_TEXT_ANTIALIAS_ON)

g.setRenderingHints(rh)

We apply text antialiasing on the painting.

g.setFont(Font("Purisa", Font.PLAIN, 13))

We specify the font name, style and point size, in which we draw the lyrics.

g.drawString("Most relationships seem so transitory", 20, 30)

The drawString() method draws the text.

Drawing text
Figure: Drawing text

In this part of the Jython Swing programming tutorial, we did some painting.