Recently I setup a new Perfect Abstractions Blog. This is a blog where myself and some of the Perfect Abstractions developers are now blogging about software and Ignition.Check it out: blog.perfectabstractions.com.
Normally images in Ignition are stored in the Ignition gateway, not in Ignition projects. This makes it easy to reuse the same images in multiple projects, but it makes projects less portable because images used in projects are not exported with projects.
So you might add your own special icons and images to Ignition and use them in your project. Later you export your project and send the project export file to a colleague. Your colleague imports the project into a different Ignition server and the images are missing. Ah man, too bad. This blog post shows how to solve this problem so that your images stay with your exported projects.
One way to solve the problem is to use SVG graphics within your projects. SVG graphics are components in Ignition and are saved in Ignition projects.
However if you want to use jpeg and/or png images in your projects and want them to be exported as part of project files, follow the steps below.
Or you can just watch this video: How to Store Images In Ignition Projects
How to store jpeg and png images in an Ignition project
Create a new client tag with the tag path and name, "project_images". Make the value of the tag an empty dataset. This client tag is where the project images will be stored.
Download this project file: ProjectImagesTemplates.proj. Click on the link to download the file.
Import that project file into your existing Ignition project using the Designer.
The project file contains two Ignition templates: The ProjectImageStorage template and the ProjectImage template.
The ProjectImagesStorage template is used to upload and manage images in your project.
The ProjectImage template is used to display images that are stored in your project.
Open the ProjectImageStorage template in the Ignition designer. Put the Designer into preview mode and click on the "Upload Image" button to find images on your local computer and upload them into the project. Give names to images at the same time you upload them.
Uploaded images will be added as rows to the "[client]project_images" tag and will appear in the table in the template. The table is bound to the "[client]project_images" tag.
It is possible to change the name of an uploaded image by double clicking in a name cell. It is possible to remove images by selecting a row in the table and pressing the "Remove Image" button.
Be sure to use the ProjectImageStorage template in the Ignition Designer and not in a client.
Drag and drop the ProjectImage template on windows and templates where you want to display images.
The ProjectImage template has a template property called, "imageName". For each ProjectImage template instance on a window or template input the name of an image as given in ProjectImageStorage template. Setting the name of an image in the imageName property will make that image display.
Save the project in the Designer. The images that are uploaded in the Designer will not be available to clients until the project is saved.
The next step is to join my email list. Just kidding, you are done. But you should join my email list if you haven't already. So you are updated about new blog posts.
The solution given in this blog post is an efficient way to store images in projects and display them multiple times across multiple windows and templates. Your .proj project export files will include your images. And when you import your projects into a different Ignition server, your images will be there, displaying and working.
A couple people have suggested to me the idea of using a memory tag instead of a client tag. A memory tag won't work because a memory tag is not exported with a project.
It is important to understand that images must be uploaded in the designer, not the client. The reason this works is because the value of a client tag in the designer (where the images get stored) becomes the default value of the client tag in all clients -- and this is how all clients get the images.
Check out this video that shows the steps: How to Store Images In Ignition Projects
Awhile ago Travis Cox gave a solution on his blog to make Java applications (and therefore Ignition clients) display nicely on high resolution monitors.
Check out his solution here: Java and High DPI Displays
I was curious if it was possible to implement the ability to drag and drop Ignition templates onto a template canvas. So I implemented it. I found that it is possible and it works. I am releasing the Template Canvas Drag and Drop project so you can have it, use it, and modify it. Download here: TemplateCanvasDragAndDrop.proj
This project shows a way for users to dynamically create Ignition screens in the runtime client by choosing templates that were created in the Ignition designer and dragging and dropping them onto a template canvas component. Additional functionality such as saving the states of screens to a database and opening them could be developed.
The Template Canvas Drag and Drop project has two windows. One window shows a list of Ignition templates that users can choose from to make a screen. Ignition automatically stores a thumbnail image of every Ignition template that is created. So the first window simply grabs the thumbnail images from the project and displays them to the user.
Users can click on a thumbnail template image and drag it onto the other window. When they drop the template the template appears in the other window where it was dropped. Then the user can select the template and move it around on the screen. I also implemented a right-click popup menu so users can right click on a template and delete it.
At the top is an "Edit" button. When this button is selected the project is in edit mode and existing templates can be clicked on and dragged around. When the button is unselected templates can be interacted with in the normal way.
The project is a good start but there is much more that can be implemented from this point. It shows that these things are possible. The ability to resize templates with the mouse can be implemented. The ability to save user-created windows, close them and open them can be implemented, and much more.
Here is a video that shows the project:
While dragging and dropping templates on a template canvas does work I admit that it is not ideal. There is one main annoyance: a change to the template canvas causes all templates to be reloaded. So if you just want to change the position of one template or add/delete one template, then all the templates get reloaded and there is a flash of a "Loading" message. Implementing configuration functionality on templates might be difficult because templates loose their state when the template canvas changes. If you can live with these problems then drag and drop with a template canvas can work.
A better solution would be to have a scripting function that can dynamically create template instances and add them to containers. This is something I've been thinking of working on. I am curious how many people would want this. I would add it to the Power Scripting Module.
The Perfect Abstractions Power Scripting Module has been tested and released for Ignition 7.9.
The module can be downloaded from this webpage: http://www.perfectabstractions.com/products.html
Contact me to get a license.
LafargeHolcim's Technical Information System Ignition project was recently featured in the Discover Gallery at the 2016 Ignition Community Conference.
The user interface in this project was developed by Perfect Abstractions. You can see a video about it at this link: Ignition Helps Huge Company Empower Thousands of Users
Here's a brief description of some of the interesting things we did:
Dynamic Creation of Template Instances
We displayed a list of names of Ignition templates in the client. We implemented the ability for users to drag a template from the list and drop it on a window. An instance of the template would be dynamically created and placed where the user dropped it. After that the user could resize, move and configure the template. Whatever the user did it could be saved and used later. We essentially created a simplified Ignition designer in the Ignition client that regular users could use to create and save functionality. You can see this functionality in the video.
Graphics Library Integrated Into Ignition
We integrated a graphics library into Ignition that shows how things are connected to each other in a graphical, animated and interactive way. You can see this functionality in the video.
Excel in Ignition
We heavily customized an Ignition Power Table to behave and function like an Excel spreadsheet. Users can add columns and rows. Users can input values and create formulas. Users can do various text formatting like change font, color, height, change text alignment and more. Users can also drop Ignition tags into cells and see real-time values. And more can be done. You can see this functionality in the video.
This week Ignition 7.9 was released. You can find out about the new features at this link: What's New.
A Perfect Abstractions Ignition Project Developer recently made a graphical interface for displaying all the available super classes and methods that exists in a component.
Sometimes it is very useful for a developer to see all that can be done with a particular component. This new object inspector helps a developer do that.
The functionality exists in a single template called TreeObjectInspector. It works directly in the Ignition designer and in clients. The template is freely available and can be imported into your own projects. The template works in Ignition 7.8.0 and up. Download here: TreeObjectInspector.proj.
Here is a screenshot of the template displaying the super classes and methods of the button component in Ignition:
The Vision Module, which is installed in Ignition by default, enables people to create Java-based graphical user interfaces. The underlying Java library that Vision Module GUIs are created upon is Java Swing. Java Swing is a standard graphics library that ships with Java, so you can learn all about it on the Internet and in books.
Because Vision Module-based GUIs are built on top of Java Swing, they follow the rules of Java Swing.
A primary rule of Java Swing is that the user interface is single threaded.
This single thread is called the Event Dispatch Thread or EDT. Code that touches the user interface is supposed to execute in the EDT. This paradigm eliminates numerous multithreading problems and hassles by not having multiple threads touch the GUI.
All the component event handling code in Ignition and the extension functions on components run in the EDT by default so you don't have to do anything special to make your code execute in the EDT. It is the default.
But if this was the end of the story there would be a big problem in Ignition GUI development. The GUI should be and needs to be very very fast - always. A slow, unresponsive GUI (even if it is just a little slow) means that a programmer didn't program it to be fast.
Some code is a little slower than very very fast, and some code is a lot slower, and there is no way around it. Database queries, requests over the network, computations over large sets of data, all these things can take awhile.
If long running code executes in the EDT then the GUI will freeze and appear slow. This is obvious since the EDT can't do anything else until it is done doing what it is currently doing. So if the EDT needs to make a database query (or other such thing) over a potentially slow network the GUI will freeze while this happens and the user will feel like the application is slow or broken.
Executing code that directly affects the GUI such as setting values on component properties, moving components, changing the display of components in some way, and configuring components is usually very fast. It is actually the non-GUI code that is most often slow, like getting data over a network and processing lots of data.
So the question is, How do you make a very fast GUI always when you have code that takes awhile to execute? The answer is to separate the slow (data processing or data retrieval) code from the fast GUI manipulation code. Put the slow code in a background thread. When the slow code completes execution have the background thread pass the data results to code in the EDT that configures and updates the GUI with that data.
Ignition provides the system.util.invokeAsynchronous(func) function to execute a function in a background thread. And Ignition provides the system.util.invokeLater(func) function to pass data produced in a background thread back into the EDT so the data can be used to update GUI components.
Here is a simple example:
#This code could be on an actionPerformed event of a button to retrieve, process and display data. #This code will prevent the GUI from freezing if the code to get and process the data takes awhile. def async(): #Fetch and process a lot of data. Return a useful dataset dataset = functionThatTakesALongTimeToFinish() def later(): label = event.source.parent.getComponent("Label") label.text = "Processed and retrieved %s results."% dataset.getRowCount() event.source.parent.getComponent("Table").data = dataset system.util.invokeLater(later) system.util.invokeAsynchronous(async)
In the code example above the async function is executed in a background thread. The async function takes its time fetching and processing data. During this time the GUI is responsive and not frozen. Then the async function calls the system.util.invokeLater function, passing the function named 'later' as an argument. The system.util.invokeLater executes its argument (the function called later) in the EDT. Notice that function later can access and use the dataset variable that was created in the async function.
It is possible to sprinkle a background thread with multiple calls to system.util.invokeLater in order to update the user interface as the background thread makes progress. This keeps the user informed of the progress of something happening.
Note that the runScript expression function in Ignition executes in the EDT. So runScript should only execute Python code that is fast and not do things that is potentially slow like execute database queries or otherwise access the network.
The PA Power Scripting Module makes writing code that uses system.util.invokeLater and system.util.invokeAsynchronous easier with @run.
The PA Office Document Module has been updated to work with Ignition 7.8. Download here