Vision - Paintable Canvas
Component Palette Icon:
Descriptionβ
The Paintable Canvas component can be custom "painted" using Jython scripting. By responding to the component's repaint Event Handler, a designer can use Java2D to draw anything within the component's bounds. Whenever any dynamic properties on the component change, the component is re-painted automatically, making it possible to create dynamic, vector-drawn components that can represent anything.
This component is an advanced component for those who are comfortable using scripting. It is not user-friendly, but it is extraordinarily powerful, as your imagination is the only limit with what this component can be.
When you first drop a Paintable Canvas onto a window, you'll notice that it looks like a placeholder. If you switch the Designer into preview mode, you'll see an icon of a pump displayed. The pump is displayed because of the pre-loaded code in the repaint Script Editor. By editing the script, you can dissect how the pump was drawn. You will notice that the script uses Java2D. You can read more about Java2D here. You will notice that as you resize the pump, it scales accordingly in preview mode. Java2D is a vector drawing library, enabling you to create components that scale gracefully.
- You can add dynamic properties to this component and use the styles feature. Use the values of dynamic properties in your repaint code to create a dynamic component. The component will repaint automatically when these values change.
- You can create an interactive component by responding to mouse and keyboard events.
- You can store your custom components on a custom palette and use them like standard components.
Propertiesβ
Name | Description | Property Type | Scripting | Category |
---|---|---|---|---|
Background Color | The background color of the component. Can be chosen from color wheel, chosen from color palette, or entered as RGB or HSL value. See Color Selector. | Color | .background | Appearance |
Border | The border surrounding this component. Options are No border, Etched (Lowered), Etched (Raised), Bevel (Lowered), Bevel (Raised), Bevel (Double), and Field Border. Note: The border is unaffected by rotation. Changed in 8.1.21 As of 8.1.21, Button Border and Other Border are no longer available. | Border | .border | Common |
Cursor | The mouse cursor to use when hovering over this component. Options are Default, Crosshair, Text, Wait, Hand, Move, SW Resize, or SE Resize. | int | .cursorCode | Common |
Focusable | If the component is focusable, it will receive keyboard input and can detect if it is the focus owner. | boolean | .focusable | Behavior |
Font | Font of text on this component. | Font | .font | Appearance |
Foreground Color | The foreground color of the component. See Color Selector. | Color | .foreground | Appearance |
Mouseover Text | The text that is displayed in the tooltip which pops up on mouseover of this component. | String | .toolTipText | Common |
Name | The name of this component. | String | .name | Common |
Quality | The data quality code for any Tag bindings on this component. | QualityCode | .quality | Data |
Styles | Contains the component's styles. | Dataset | .styles | Appearance |
Visible | If disabled, the component will be hidden. | boolean | .visible | Common |
Scriptingβ
Component Functionsβ
This component does not have component functions associated with it.
Extension Functionsβ
This component does not have extension functions associated with it.
Event Handlersβ
Event handlers allow you to run a script based off specific triggers. See the full list of available event handlers on the Component Events page
Customizersβ
Examplesβ
Drawing Custom Imagesβ
It is 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 to create a completely custom image. Below are script examples illustrating this functionality.
from java.awt import Color
from java.awt.geom import Rectangle2D
# Specify the rectangle's size in pixels
width = 100
height = 50
# Specify its position, in pixels, relative to the top left edge of the component
x = 20
y = 30
# Create the rectangle using the specs from steps 1 and 2.
rectangle = Rectangle2D.Float( x, y, width, height)
# Set the color and draw the rectangle
g = event.graphics
g.setColor(Color.GREEN)
g.draw(rectangle)
# 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.
from java.awt import Color
from java.awt.geom import GeneralPath
# Get the shape coordinates from a dataset
coordinates = event.source.points
# Create an arbitrary shape
shape = GeneralPath()
# Create a starting point from the 0-th row of the dataset
shape.moveTo(coordinates.getValueAt(0, "xCoord"), coordinates.getValueAt(0, "yCoord"))
# 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"))
# Close up the shape
shape.closePath()
# Set the color and draw the shape.
g = event.graphics
g.setColor(Color.GREEN)
g.draw(shape)
Additionally, you can specify the line color, stroke style, fill color and gradient for the shape. Refer to the following Oracle tutorials for more infomation:
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β
The following script shows how to retrieve an image stored as a BLOB in a SQL database and display it using the Paintable Canvas component. 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
# 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)
# Read the image from the bytes
image = ImageIO.read(ByteArrayInputStream(bytes))
# Draw the image
event.graphics.drawImage(image,0,0,event.source)
Displaying an Image from a URLβ
The script below shows how to retrieve and display an image from a URL. The URL can be hard-coded, or read from a custom property.
from java.net import URL
from javax.imageio import ImageIO
# Obtain the URL string.
imageURL = "your/URL/here"
# Create a Java URL object from the URL string
url = URL(imageURL)
# Read the image at the URL
image = ImageIO.read(url)
# Display the image, aligned to the top left corner
event.graphics.drawImage(image, 0, 0, event.source)
When displaying existing images, make sure to consider their original size. If the image does not have the same dimensions as the size of the Paintable Canvas component, it will either be cropped or only partially fill the Paintable Canvas component depending on if itβs larger or smaller than the Paintable Canvas.
However, you can scale the image using the scale
method of the Graphics2D object. Refer to Scaling, Zooming, and Rotating for examples.
Drawing Textβ
If you want to display text on the Paintable canvas by itself or to label images, Java Graphics2D library provides the drawString
method. 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
# Specify the string to display
string = "Some string to display."
# Specify X and Y coordinates of the lower left point for the displayed text
startingX = 50
startingY = 10
# Set the color and draw the string
g = event.graphics
g.setColor(Color.BLUE)
g.drawString(string, startingX, startingY)
Scaling, Zooming, and Rotatingβ
You can scale an image by simply defining the scale factor or calculating the width and height values to dynamically match the component size. Dynamically scaling an image is most helpful when the ratio of the image size to the component size is not known in advance.
# Get a reference to the java.awt.Graphics2D object
g = event.graphics
# Define the scale factor, and scale the image horizontally and vertically
scaleFactor = 2.0
g.scale(scaleFactor, scaleFactor)
# Draw the image, aligned ot the top left of the component
g.drawImage(image,0,0,event.source)
# Get the width and height of the BufferedImage object returned from ImageIO.read call:
imageW = image.getWidth()
imageH = image.getHeight()
# Get the width and height of the Paintable Canvas component
canvasW = event.width
canvasH = event.height
# Calculate the scale factors;
# Account for 1 pixel of the component's border dimensions
scaleX = (canvasW-1.0) / imageW
scaleY = (canvasH-1.0) / imageH
# Apply scaling and draw the image
g = event.graphics
g.scale(scaleX, scaleY)
g.drawImage(image,0,0,event.source)
The same dynamic technique as the scaling option above can be used to dynamically control the zoom factor via a custom property. The following example implies that the Paintable Canvas component has an Integer type custom property called zoom
containing the zoom percentage (200 for zooming by 200%).
from javax.imageio import ImageIO
from java.io import ByteArrayInputStream
# 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)
# Read the image from the bytes
image = ImageIO.read(ByteArrayInputStream(bytes))
# Get the scale factor and convert the Integer percent value to a Float scale factor
scaleFactor = event.source.zoom * 0.01
# Scale and draw the image
g = event.graphics
g.scale(scaleFactor, scaleFactor)
g.drawImage(image,0,0,event.source)
This last example shows how to rotate the image based on rotation center and angle values. The rotation center coordinates and the rotation angle value are obtained from the component's custom properties. With this, user controls like sliders, can be provided to dynamically control rotation.
from javax.imageio import ImageIO
from java.io import ByteArrayInputStream
# 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)
# Read the image from the bytes
image = ImageIO.read(ByteArrayInputStream(bytes))
# Get the center and angle of rotation from custom propterties
rotationCenterX = event.source.rotationCenterX
rotationCenterY = event.source.rotationCenterY
rotationAngle = event.source.rotationAngle
# Draw the image
g = event.graphics
g.rotate(rotationAngle, rotationCenterX, rotationCenterY)
g.drawImage(image,0,0,event.source)