Database and Paintable Canvas Refresh

My application is visualizing images from Database. I am using Paintable Canvas to view images, but the image doesn´t refresh when I selected registers continuously from database table

.

Here is my code in Paintable Canvas:

from javax.imageio import ImageIO
from java.io import ByteArrayInputStream

bytes = event.source.getClientProperty(“img-bytes-cached-name”)

id = event.source.parent.parent.getComponent(‘Documents’).getComponent(‘Documents’).id

blob = system.db.runPrepQuery(“SELECT file FROM magna_silao.documents where id= ?”, [id])

bytes = blob[0][0]

event.source.putClientProperty(“img-bytes-cached”,bytes)

image = ImageIO.read(ByteArrayInputStream(bytes))

event.graphics.drawImage(image,0,0,event.source)

event.source.repaint()

Any idea regarding Paintable Canvas refresh images?

Where is this code? In the paint method? If so, break it up so your db query runs separately (on some propertyChange) to obtain and set the client property. Say, on your Documents table, have a propertyChange that does most of the work, but only on image change, like so:

if event.propertyName == 'id':
	from javax.imageio import ImageIO
	from java.io import ByteArrayInputStream

	blob = system.db.runPrepQuery(“SELECT file FROM magna_silao.documents where id= ?”, [event.newValue])
	bytes = blob[0][0]
	image = ImageIO.read(ByteArrayInputStream(bytes))
	canvas = event.source.parent.getComponent("Paintable canvas")
	canvas.putClientProperty("cached-image", image)
	canvas.repaint()

Then your paint event only has to paint, and only if the cached image has been processed:

image = self.getClientProperty(“cached-image”)
if image:
	event.graphics.drawImage(image, 0, 0, event.source)

Note that painting can be called really often – any time the anything obscures the canvas or updated. Or various window/container events.

{ Also, when pasting code or other plain text in the forum, use a line with just three backquotes above the code, and again below the code, so it formats nicely. }

3 Likes

Two errors when the application is executed:

Traceback (most recent call last):
File “event:propertyChange”, line 9, in
AttributeError: ‘NoneType’ object has no attribute ‘putClientProperty’

Traceback (most recent call last):
File “event:repaint”, line 4, in
NameError: name ‘self’ is not defined

That means your paintable canvas is not named "Paintable Canvas", or is not a sibling of your table.

Sorry, the "self" should be event.source.

2 Likes

Works, but Paintable Canvas continues without refresh image when I selected others database registers.

Additionally, the client app sends as following popup error:

Traceback (most recent call last):
File “event:propertyChange”, line 6, in
IndexError: Row index 0 out of range.

{if event.propertyName == ‘id’:
from javax.imageio import ImageIO
from java.io import ByteArrayInputStream

blob = system.db.runPrepQuery("SELECT file FROM magna_silao.documents where id= ?", [event.newValue])
bytes = blob[0][0]

image = ImageIO.read(ByteArrayInputStream(bytes))
canvas = event.source.parent.getComponent("Paintable Canvas")
canvas.putClientProperty("cached-image", image)
canvas.repaint()}

If your dataset is empty, there's no blob[0][0]. You'll have to check the returned rowCount in your propertyChange event.

1 Like

I seem to be doing many things wrong here, any tips to get this to work?

You’ve got fancy non-ASCII quotes on line five from copy-pasting from the forum (notice the syntax highlighting, or lack thereof). Replace them with standard ".

1 Like

Seems I had picked up the bad quotes from that topic’s OP. ):

Great catch, thanks! Now that part works, however the paintable canvas only pulls up the default image when I select a value. I assume I have to put something besides the default scripting into the repaint, is that correct?

Yes. Something similar to the three lines in the same comment where you got the rest.

It looks like I’m still hitting one error:

Here is the script running on the dropdown component in property change:

#print event.propertyName
if event.propertyName == 'selectedStringValue':
	from javax.imageio import ImageIO
	from java.io import ByteArrayInputStream
	blob = system.db.runPrepQuery("SELECT ImageSource FROM Image_Classification_Master WHERE ImagePath= ?", [event.newValue])
	bytes = blob[0][0]
	image = ImageIO.read(ByteArrayInputStream(bytes))
	canvas = event.source.parent.getComponent("Paintable Canvas")
	canvas.putClientProperty("cached-image", image)
	canvas.repaint()

That’s purely a database error. It doesn’t like your table name.

Hmm, is it the table name it doesn’t like or some other syntax issue? When I use the DB Query browser, it accepts that table name. Maybe I need to specify the DB source name, since it is not the default DB?

Would I add the database like this?

#print event.propertyName
if event.propertyName == 'selectedStringValue':
	from javax.imageio import ImageIO
	from java.io import ByteArrayInputStream
	blob = system.db.runPrepQuery("SELECT ImageSource FROM Image_Classification_Master WHERE ImagePath= ?", [event.newValue], "MyDatabaseName")
	bytes = blob[0][0]
	image = ImageIO.read(ByteArrayInputStream(bytes))
	canvas = event.source.parent.getComponent("Paintable Canvas")
	canvas.putClientProperty("cached-image", image)
	canvas.repaint()
1 Like

Hello again,

The general functionality seems to be working now, however the images are just showing up as a blank white image. I’m using SQL server, if that makes a difference, I’m not sure. You can see my script for the dropdown in my last post. On the paintable canvas, I have the code from @pturmel above in the repaint:

image = self.getClientProperty(“cached-image”)
if image:
	event.graphics.drawImage(image, 0, 0, event.source)

Hello,

I figured out the issue, the image was being pulled properly, but the canvas was too small to show it properly, so I was only seeing the blank white corner of the image. I found a way to scale it to the canvas in this post:

https://support.inductiveautomation.com/hc/en-us/articles/360047155032-Working-with-the-Paintable-Canvas-Component#scale-and-zoom

Here is the script for the repaint of the canvas:

image = event.source.getClientProperty("cached-image")
if image:
	# 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)
	
#previous code, kept for reversion only	
#if image:
#	event.graphics.drawImage(image, 0, 0, event.source)

Thanks,

4 Likes