Is it okay to use Swing components instead of PMIObjects? & Using the setStyles() method

Hi Everyone,

I have a question about PMIObjects. According to the Javadocs, PMIObjects like PMILabel inherit from Swing components—in this case, from JLabel.

I’m building some custom widgets and have noticed that my styles are ignored when I use PMIObjects. However, when I use Swing components, the styles work as expected. Am I using incorrect syntax for PMIObjects? Is it okay to use Swing components instead of PMIObjects? Are there any risks associated with using Swing components?

Additionally, I have a question about using the setStyles() method for any PMIObject.

I’m using a dataset builder, but I’m unsure how to define the column types. I don’t know which class to use for defining the column types. Can anyone help me create a styles dataset from inside the module? Here is a screenshot from Ignition Designer when using a style customizer:

public class DiscreteControlPane extends JPanel implements ActionListener {

    private final Logger logger = LoggerFactory.getLogger(getClass());
    private PMIButton autoOn = new PMIButton();
    //private JLabel deviceState = new JLabel();
    private PMILabel deviceState = new PMILabel();
    private PMIButton manualOn = new PMIButton();
    private PMIButton manualOff = new PMIButton();
    private PMILabel deviceDescription = new PMILabel();

    private DatasetBuilder styles = DatasetBuilder.newBuilder()
            .colNames("Property","Value")
            .colTypes(String.class, String.class)
            .addRow("border", "border(line;color(0,0,0,255);2)")
            .addRow("background", Color.blue);


    private Dataset stylesDataset = styles.build();

    private String tagPath;
    private Color lightGrey = new Color(238, 236, 232, 255);

    private Dimension buttonSize = new Dimension(150, 53);

    public DiscreteControlPane() {

        logger.info("Styles -> ",deviceDescription.getStyles());

        logger.info("Inside Constructor for discrete pane -> Context: ");
        this.setBackground(lightGrey);
        this.setOpaque(true);
        GridBagLayout layoutManager = new GridBagLayout();

        this.setLayout(layoutManager);
        setPreferredSize(new Dimension(520, 303));
        GridBagConstraints layoutBehavior = new GridBagConstraints();
        layoutBehavior.weighty = 1;
        layoutBehavior.weightx = 1;
        layoutManager.setConstraints(this, layoutBehavior);

        autoOn.setText("AUTO ON");
        manualOn.setText("MANUAL ON");
        manualOff.setText("MANUAL OFF");
        deviceState.setText("INSERT DYNAMIC TEXT");
        deviceDescription.setText("INSERT DYNAMIC TEXT");

        autoOn.addActionListener(this);
        manualOn.addActionListener(this);
        manualOff.addActionListener(this);


        layoutBehavior.gridx = 0;
        layoutBehavior.gridy = 0;
        layoutBehavior.gridwidth = GridBagConstraints.REMAINDER;
        layoutBehavior.fill = GridBagConstraints.CENTER;
        layoutManager.setConstraints(deviceDescription, layoutBehavior);
        add(deviceDescription);



        //TODO: resize buttons - look into padding, why cant my elements retain their size....
        
        deviceState.setStyles(stylesDataset);
        //deviceState.setBackground(new Color(0x1F3D7C));
        deviceState.setOpaque(true);
        layoutBehavior.gridx = 0;
        layoutBehavior.gridy = 1;
        layoutBehavior.gridwidth = GridBagConstraints.REMAINDER;
        layoutBehavior.fill = GridBagConstraints.CENTER;
        layoutManager.setConstraints(deviceState, layoutBehavior);
        add(deviceState);

        layoutBehavior.insets = new Insets(10, 20, 20, 20);

        autoOn.setPreferredSize(buttonSize);
        layoutBehavior.gridx = 0;
        layoutBehavior.gridy = 2;
        layoutBehavior.gridwidth = 1;
        layoutBehavior.fill = GridBagConstraints.BOTH;
        layoutManager.setConstraints(autoOn, layoutBehavior);
        add(autoOn);

        layoutBehavior.insets = new Insets(0, 0, 20, 20);
        manualOff.setPreferredSize(buttonSize);
        layoutBehavior.gridx = 2;
        layoutBehavior.gridy = 3;
        layoutBehavior.gridwidth = 1;
        layoutBehavior.fill = GridBagConstraints.BOTH;
        layoutManager.setConstraints(manualOff, layoutBehavior);
        add(manualOff);

        layoutBehavior.insets = new Insets(10, 0, 20, 20);
        manualOn.setPreferredSize(buttonSize);
        layoutBehavior.gridx = 2;
        layoutBehavior.gridy = 2;
        layoutBehavior.gridwidth = 1;
        layoutBehavior.fill = GridBagConstraints.BOTH;
        layoutManager.setConstraints(manualOn, layoutBehavior);
        add(manualOn);
      
    }

I don't really have an answer as far as using Swing Components over PMI objects, technically it should work just fine. I'm guessing the recommendation would be to use the PMI objects so that there is a similar UI Look and Feel for your component in comparison to the standard components. The user is going to have an expectation about how certain things work, and generally you don't want to subvert that expectation.

As for the Dataset builder, for the Style dataset that you've shown I would expect something like this:

import javax.swing.border.Border
import javax.swing.border.LineBorder
import java.awt.Color

private DatasetBuilder styles = DatasetBuilder.newBuilder()
    .colNames("State","animationIndex","animationDuration","background","border","fillBackground","foreground")
    .colTypes(Integer.class, Integer.class,Integer.class,Color.class,Border.class,Boolean.class,Color.class)
    .addRow(0,0,1000,Color.blue,LineBorder(Color.black,2),true,Color.black);

Thanks for responding. I tried your suggested changes but no dice. Any suggestions why the PMILabel ignores styles but the JLabel will apply the styles? does the setStyles() method work?


import com.inductiveautomation.factorypmi.application.components.PMIButton;
import com.inductiveautomation.factorypmi.application.components.PMILabel;
import com.inductiveautomation.ignition.common.Dataset;
import com.inductiveautomation.ignition.common.util.DatasetBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.border.LineBorder;


//Delete VisionComponent interface. Make be causing null pointer exceptions.
public class DiscreteControlPane extends JPanel implements ActionListener {

    private final Logger logger = LoggerFactory.getLogger(getClass());
    private PMIButton autoOn = new PMIButton();
    //private JLabel deviceState = new JLabel();
    private PMILabel deviceState = new PMILabel();
    private PMIButton manualOn = new PMIButton();
    private PMIButton manualOff = new PMIButton();
    private PMILabel deviceDescription = new PMILabel();

    private DatasetBuilder styles = DatasetBuilder.newBuilder()
            .colNames("State", "background", "border", "fillBackground", "foreground")
            .colTypes(Integer.class,Color.class, Border.class, Boolean.class,Color.class)
            .addRow(0,Color.blue, new LineBorder(Color.black,2),true, Color.BLACK);



    private Dataset stylesDataset = styles.build();

    private String tagPath;
    private Color lightGrey = new Color(238, 236, 232, 255);

    private Dimension buttonSize = new Dimension(150, 53);

    public DiscreteControlPane() {

        logger.info("Styles -> ",deviceDescription.getStyles());

        logger.info("Inside Constructor for discrete pane -> Context: ");
        this.setBackground(lightGrey);
        this.setOpaque(true);
        GridBagLayout layoutManager = new GridBagLayout();

        this.setLayout(layoutManager);
        setPreferredSize(new Dimension(520, 303));
        GridBagConstraints layoutBehavior = new GridBagConstraints();
        layoutBehavior.weighty = 1;
        layoutBehavior.weightx = 1;
        layoutManager.setConstraints(this, layoutBehavior);

        autoOn.setText("AUTO ON");
        manualOn.setText("MANUAL ON");
        manualOff.setText("MANUAL OFF");
        deviceState.setText("INSERT DYNAMIC TEXT");
        deviceDescription.setText("INSERT DYNAMIC TEXT");

        autoOn.addActionListener(this);
        manualOn.addActionListener(this);
        manualOff.addActionListener(this);


        layoutBehavior.gridx = 0;
        layoutBehavior.gridy = 0;
        layoutBehavior.gridwidth = GridBagConstraints.REMAINDER;
        layoutBehavior.fill = GridBagConstraints.CENTER;
        layoutManager.setConstraints(deviceDescription, layoutBehavior);
        add(deviceDescription);



        //TODO: resize buttons - look into padding, why cant my elements retain their size....
        
        deviceState.setStyles(stylesDataset);
        deviceState.repaint();
        //deviceState.setBackground(new Color(0x1F3D7C));
        deviceState.setOpaque(true);
        layoutBehavior.gridx = 0;
        layoutBehavior.gridy = 1;
        layoutBehavior.gridwidth = GridBagConstraints.REMAINDER;
        layoutBehavior.fill = GridBagConstraints.CENTER;
        layoutManager.setConstraints(deviceState, layoutBehavior);
        add(deviceState);

        layoutBehavior.insets = new Insets(10, 20, 20, 20);

        autoOn.setPreferredSize(buttonSize);
        layoutBehavior.gridx = 0;
        layoutBehavior.gridy = 2;
        layoutBehavior.gridwidth = 1;
        layoutBehavior.fill = GridBagConstraints.BOTH;
        layoutManager.setConstraints(autoOn, layoutBehavior);
        add(autoOn);

        layoutBehavior.insets = new Insets(0, 0, 20, 20);
        manualOff.setPreferredSize(buttonSize);
        layoutBehavior.gridx = 2;
        layoutBehavior.gridy = 3;
        layoutBehavior.gridwidth = 1;
        layoutBehavior.fill = GridBagConstraints.BOTH;
        layoutManager.setConstraints(manualOff, layoutBehavior);
        add(manualOff);

        layoutBehavior.insets = new Insets(10, 0, 20, 20);
        manualOn.setPreferredSize(buttonSize);
        layoutBehavior.gridx = 2;
        layoutBehavior.gridy = 2;
        layoutBehavior.gridwidth = 1;
        layoutBehavior.fill = GridBagConstraints.BOTH;
        layoutManager.setConstraints(manualOn, layoutBehavior);
        add(manualOn);
  
    }

I'm sure it does, but it seems that perhaps there is something calling this at the creation of the component (in the designer).

If you look at the documentation for the StyleProvider Interface that defines the setStyles function it says this:

Typically, the implementation of the styles is provided by the StyleListener class, which sets up all of the bindings.

Which makes me think that really the style is expected to be modified in a different place.

Thinking through this again, styles (in a Vision Component) context are really something that are changed by the end user. As the component designer, your task should really be to just provide support (which has already been done by the PMI components).

What exactly are you trying to accomplish with the styles, perhaps there is a different approach that should be used.

We are modularizing some control interfaces for some of our machines. The look and feel will be consistent with stuff we originally created in designer. This label will be subscribed to a tag and change its style based off the value that is read. Its the same functionality that I currently have on the project but I want to package this inside my module. I don't mind using a JLabel since it does what I need it to do but it would of been nice to make a dataset instead.