Dynamically creating objects

Is there any way to dynamically create objects at run-time? I’m thinking of something like a factory overview page with custom icons pasted at locations read from a database.

Al

Sorry, this is currently an unsupported feature. You cannot add components dynamically in the runtime.

You can, however, use the Paintable Canvas component to draw shapes or images at different points on the screen. For this to work, the component needs to be the same size as the root container and anchored to all 4 sides. Here is an example repaint script that paints circles at random locations on the screen:[code]from java.awt import Color
from java.awt.geom import Ellipse2D
from java.util import Random

g = event.graphics

Create a circle with set width and height

circleHeight = 16.0
circleWidth = 16.0
circle = Ellipse2D.Float(0,0,circleWidth,circleHeight)

random = Random()
for i in range(10):
#### Move to a x,y location and paint circle
x = random.nextInt(500)
y = random.nextInt(500)
g.translate(x,y)
g.setColor(Color.GREEN)
g.fill(circle)
g.setColor(Color.GREEN.darker())
g.draw(circle)
g.translate(-x,-y) # Make sure you move back to starting point[/code]Here is an example repaint script that paints images at random locations on the screen:[code]from java.awt import Color
from javax.swing import ImageIcon
from java.util import Random
from java.net import URL

gatewayAddress = fpmi.system.getGatewayAddress()

g = event.graphics

Create an image with a specified width and height

image = ImageIcon(URL(“http://%s/gateway/images/Builtin/icons/16/lightbulb_on.png” % (gatewayAddress)))

random = Random()
for i in range(10):
#### Move to a x,y location and paint circle
x = random.nextInt(500)
y = random.nextInt(500)
g.drawImage(image.getImage(), x, y, None)[/code] Hope this helps.

Hi Travis,

I tried your examples and they worked well, although setting the paintable canvas to the front of the z-order so it appeared as an overlay on top of other controls could result in rather unpredictable results. For example, if you place a Text Area control on the screen, scrolling the text area at runtime resulted in lots of circles/images being drawing at random over it. I would imagine this has something to do with the control repaint conflicting with the paintable canvas repaint. This problem seemed to disappear if I changed the paintable canvas code to put objects into predefined positions.

The problem with this approach is that you do not the advantages of treating everything like objects with their own events. If I continue with this approach I would have to manually draw everything, then manually deal with events like detecting when a user clicks on an icon etc. (As an aside, I was looking for some way to detect where the user pressed the mouse and came across the undocumented fpmi.gui.convertPointToScreen function in one of Carl’s downloads in this post.) When doing this I found that the paintable canvas ‘absorbed’ all of the mouse clicks, meaning other controls underneath it did not respond. Also, I was getting the absolute screen position, not the position within the paintable canvas - is the latter possible?

Whilst the paintable canvas is undoubtedly very powerful, it does seem something of a backward step in this particular application, compared to instantiating and manipulating pre-defined objects.

I was thinking a better approach may be to have a ‘stock’ of pre-defined objects in the Window with their Visible property set to false - if they are required, they are put in the correct position at the correct size and made visible.

Al

I’ve managed to get the position of the mouse click within the paintable canvas by using event.getX() and event.getY() within the paintable canvas mousePressed event. I found these methods by looking at Java documentation, but I don’t know enough to know whether this is the correct approach.

Al

I’m not technical enough in the Java2D arena - Carl, Travis, Kevin, or Colby should be able to help out. I think you’re really stretching the functionality of the Paintable Canvas component, which is a good thing. What you really need is Native 2D drawing (coming) and the ability to instantiate multiple components (maybe). Lots of good development going on in that direction - I don’t think you’ll be disappointed.

Al, I’ve done something with the paintable canvas that does pretty much what you describe. I can post it later (it’s on another PC), but this is the approach:

  1. A global script creates a basic drawing.
  2. The script is called in my paint component, and a bufferedImage is created (this is not 100% necessary, but if you have a lot of components, it’s blazingly fast).
  3. The bufferedImage is added to the canvas, followed by dynamic properties painted on top of the image, such as text or colors. These dynamic properties are passed into the function as a list, and can be bound to tags or whatever.
  4. You had the right idea about getting the x/y position of the objects to generate events. What I do is store the click coordinates as a container property (not the canvas, though) and then trigger a repaint of the canvas. Then, as I loop through my components on the canvas, the one that lays under the click coordinates is the “chosen one”. Just like I pass dynamic properties into the canvas, I then pass dynamic events back out, so I can treat my canvas objects just like any other icon or button. The sky is the limit.
  5. There was another thread about controlling the layering order, so you might need to do a little planning to get the results you need, but it worked very well for my purposes. Also, if you use the bufferedImage approach, pay attention to some of the properties that affect how he image is displayed.

Like I said, I’ll post what I’ve got later. I’d like to have someone else come up with more ideas for this.

Al, I’ve attached a few things if you’re interested. It’s mostly stuff I am playing around with, and is far from being a true “dynamic object library”, but could be made into something pretty useful for a screen that has a lot of objects that change state a lot. By adding more drawing scripts, each iteration could be a different object if the object type was passed in. I documented the code fairly well, but it’s pretty straightforward anyway. I’m not an expert, and I’m sure there are better ways to do it, but it works for now.

Just import the two scripts and window, and create a table based on the column snapshot I attached. Or, change the bind properties of the root container “modList dataset” to use tags instead of a table (I didn’t have a plc for my testing). The binding is done directly on the dataset with polling off so it’s initialized on startup, but then updated on the canvas “mouse pressed” event after that. The slider can draw up to 500 pumps, so you will need at least 500 records or tag sets, or just decrease the slider max value. To make the canvas repaint, just increment the canvas “repaint” dynamic property.

I’m just using the IA Pump drawing with a few things stripped out (the dynamic stuff). After it’s drawn to an image, I paint the title and a feedback bar to indicate speed. The rotate button is there to test how well the screen will paint, as is the zoom in and zoom out buttons. If you click on a pump, it will invert 180 degrees, which is how I am testing events.

I’m working on more stuff to make it more workable, but it’s a start.
Pump12.zip (39.1 KB)

This sounds like the most pragmatic approach to me.

It is in our documentation also, here : inductiveautomation.com/prod ... .htm#mouse

Step 7,

Thanks for posting your code :thumb_left: . Unfortunately I get an error if I try to increase the number of pumps - an ‘index out of range’ error at line 101 in drawPump. Maybe something to do with the values in the TestModules table. I can display one pump however and see it respond to a mouse click.

I can appreciate the raw power this kind of thing gives, but my natural laziness wants an easier way!

Al

Carl,

Just call me pragmaticAl from now on :wink:

I see the entry in the manual giving mouse event x and y coords (cunningly accessed using event.x and event.y). You’re going to have to lower your standard of service and force us to read the manual :slight_smile:

Ah ha! Is that how we get you guys to read it? Ok, we’ll have to work on that…