Knowledgebase:

Working with the Paintable Canvas Component

Posted by Anna Christian, Last modified by Peggie Wong on 01/24/2020 02:09 PM

The Paintable Canvas is a very powerful advanced component that allows displaying custom graphic content using the Java Graphics2D API. Basic information about the component is available in the Appendix/Components/Misc/Paintable Canvas section of Ignition user manual. This article offers additional use cases and examples.

The prerequisites for working with the examples below are:

  • Familiarity with scripting in general, and with event scripting in Ignition in particular.
  • At least a basic familiarity with the Java programming language and with using Java in Jython scripting.
  • An ability to read and work with Java API documentation.

Contents:

  1. Working with existing images
  2. Drawing custom images
  3. Drawing text
  4. Scaling, Zooming, and Rotating

1. Working with Existing Images

One of the most common use cases for the Paintable Canvas component is displaying images retrieved either from a database table using a SQL query, or from a file server using the image URL.

Displaying an Image Stored in a Database Table

Purpose Code
Retrieve an image stored as a BLOB in a SQL database, and display it using the Paintable Canvas component.
Note: the query in this example relies on a specific SQL table structure. Make sure to adjust the query, including table and column names, as well as any filtering, to match your table structure and needs.
from javax.imageio import ImageIO
from java.io import ByteArrayInputStream

# 1. See if the image is cached; if not, load it from the database
bytes = event.source.getClientProperty("img-bytes-cached")

if not bytes:
    query = "SELECT image_blob FROM imagestore limit 1"
    bytes = system.db.runScalarQuery(query)
    event.source.putClientProperty("img-bytes-cached",bytes)

# 2. Read the image from the bytes
image = ImageIO.read(ByteArrayInputStream(bytes))

# 3. Draw the image
event.graphics.drawImage(image,0,0,event.source)

Displaying an Image from a URL

Purpose Code
Retrieve an image from a URL and display it using the Paintable Canvas component.

The URL can be hard-coded, or read from a custom property.
from java.net import URL
from javax.imageio import ImageIO

# 1. Obtain the URL string. 
imageURL = "your/URL/here"

# 2. Create a Java URL object from the URL string
url = URL(imageURL)

# 3. Read the image at the URL
image = ImageIO.read(url)

# 4. Display the image, aligned to the top left corner
event.graphics.drawImage(image, 0, 0, event.source)

Adjusting Image Size

It is quite likely that the original image you are trying to display does not have the same size / dimensions as the size of the Paintable Canvas component you want to display it in. If that is the case, you have two options:

  • Make no adjustments and display the image in its original size.
    If the image is larger than the Paintable Canvas component, the image will be cropped; if the image is smaller, it will only partially fill the component.
  • Scale the image using the scale method of the Graphics2D object. Please refer to the section on scaling and zooming for examples.

2. Drawing Custom Images

In addition to retrieving and displaying images, it is also possible to draw geometric primitives and arbitrary shapes on the Paintable Canvas. This includes points, lines, curves, ellipses, arcs, rectangles, paths, and any combinations of the above, creating a completely custom image. The default example on the repaint event handler of the Paintable Canvas is a great illustration of this functionality, showing how to draw a motor. Below are examples illustrating some of that functionality.

Purpose Code
Draw a rectangle

Other shapes, lines, curves can be drawn in a similar way. Please see this tutorial from Oracle for details.
from java.awt import Color
from java.awt.geom import Rectangle2D

# 1. Specify the rectangle's size in pixels
width  = 100
height = 50

# 2. Specify its position, in pixels, relative to the top left edge of the component
x = 20 
y = 30 

# 3. Create the rectangle using the specs from steps 1 and 2.
rectangle = Rectangle2D.Float( x, y, width, height)

# 4. Set the color and draw the rectangle 
g = event.graphics
g.setColor(Color.GREEN)
g.draw(rectangle)
Draw an arbitrary shape based on the coordinates stored in a dataset.

This example implies a dataset with each row storing the X and Y coordinates of a single point. The columns in this case are called "xCoord" and "xCoord", respectively.

Please see this tutorial from Oracle for more information on drawing arbitrary shapes.
from java.awt import Color
from java.awt.geom import GeneralPath

# 1. Get the shape coordinates from a dataset
coordinates = event.source.points

# 2. Create an arbitrary shape 
shape = GeneralPath()

# 3. Create a starting point from the 0-th row of the dataset
shape.moveTo(coordinates.getValueAt(0, "xCoord"), coordinates.getValueAt(0, "yCoord"))

# 4. Iterate through the rows of coordinates and draw the lines
for rowNdx in range(1, coordinates.rowCount):
    shape.lineTo(coordinates.getValueAt(rowNdx, "xCoord"), coordinates.getValueAt(rowNdx, "yCoord"))

# 5. Close up the shape
shape.closePath()

# 6. Set the color and draw the shape.
g = event.graphics
g.setColor(Color.GREEN)
g.draw(shape)

Additionally, you can specify the line (outline) color, stroke style (e.g. use a dashed line), fill color and gradient for the shape. Please see this tutorial from Oracle for more information.

3. Drawing Text

For the cases where it is necessary to display text on the Paintable canvas (by itself, or to label images) Java Graphics2D library conveniently provides the drawString method so you do not have to trace the letters using curves, etc. yourself. The method takes the String to draw and the X and Y coordinates of the lower left point of the string, in pixels, relative to the top left of the Paintable Canvas component.

Purpose Code
Draw text

The string given to the drawString method can be specified as a literal, obtained from a custom property, or put together programmatically.
from java.awt import Color
from java.awt.geom import GeneralPath

# 1. Specify the string to display
string = "Some string to display."

# 2. Specify X and Y coordinates of the lower left point for the displayed text 
startingX = 50
startingY = 10

#3. Set the color and draw the string
g = event.graphics
g.setColor(Color.BLUE)
g.drawString(string,  startingX, startingY)

4. Scaling, Zooming, and Rotating

Scaling and Zooming

Purpose Code
Scale an image by a factor of 2 (zoom to 200%).
# 1. Get a reference to the  java.awt.Graphics2D object
g = event.graphics

# 2. Define the scale factor, and scale the image horizontally and vertically
scaleFactor = 2.0
g.scale(scaleFactor, scaleFactor)

# 3. Draw the image, aligned ot the top left of the component
g.drawImage(image,0,0,event.source)
Dynamically control the zoom factor via a custom property.

The example implies that the Paintable Canvas component has an Integer type custom property called "zoom" containing the zoom percentage (e.g. 200 for zooming by 200%).
from javax.imageio import ImageIO
from java.io import ByteArrayInputStream

# 1. Get the image bytes from the database or cache
bytes = event.source.getClientProperty("img-bytes-cached")
if not bytes:
    bytes = system.db.runScalarQuery("SELECT image_blob FROM imagestore limit 1")
    event.source.putClientProperty("img-bytes-cached",bytes)

# 2. Read the image from the bytes
image = ImageIO.read(ByteArrayInputStream(bytes))

# 3. Get the scale factor and convert the Integer percent value to a Float scale factor
scaleFactor = event.source.zoom * 0.01

# 4. Scale and draw the image 
g = event.graphics
g.scale(scaleFactor, scaleFactor)
g.drawImage(image,0,0,event.source)

The same technique could be used to dyanmically scale an image to the size of the Paintable Canvas component, when the ratio of the image size to the component size is not known in advance.

Purpose Code
Scale an image by a horizontal and vertical and factors that are calculated based on the size of the image and the size of the Paintable Canvas component.
# 1. Get the width and height of the BufferedImage object returned from ImageIO.read call:
imageW = image.getWidth()
imageH =  image.getHeight()

# 2. Get the width and height of the Paintable Canvas component
canvasW = event.width
canvasH = event.height

# 3. Calculate the scale factors; 
#    Account for 1 pixel of the component's border dimensions 
scaleX = (canvasW-1.0) / imageW
scaleY = (canvasH-1.0) / imageH

# 4. Apply scaling and draw the image
g = event.graphics
g.scale(scaleX, scaleY)
g.drawImage(image,0,0,event.source)

Rotating

Purpose Code
Rotate the image based on rotation center and angle values.

In this example, the rotation center coordinates and the rotation angle value are obtained from the component's custom properties. With this set up, user controls, such as sliders, can be provided to dynamically control rotation.
from javax.imageio import ImageIO
from java.io import ByteArrayInputStream

# 1. Get the image bytes from the database or cache
bytes = event.source.getClientProperty("img-bytes-cached")
if not bytes:
    bytes = system.db.runScalarQuery("SELECT image_blob FROM imagestore limit 1")
    event.source.putClientProperty("img-bytes-cached",bytes)

# 2. Read the image from the bytes
image = ImageIO.read(ByteArrayInputStream(bytes))

# 3. Get the center and angle of rotation from custom propterties
rotationCenterX = event.source.rotationCenterX
rotationCenterY = event.source.rotationCenterY
rotationAngle   = event.source.rotationAngle

# 4. Draw the image
g = event.graphics
g.rotate(rotationAngle, rotationCenterX, rotationCenterY)
g.drawImage(image,0,0,event.source)					

(5 vote(s))
Helpful
Not helpful