Find and Replace: Vision Component Colors

Hi folks,

I'm looking at updating a project's color scheme (moving towards high performance HMI) and I would like to use Find&Replace (or some analog thereof) to swap existing color codes for their replacements. I've tried searching the RGB code as well as the Hex code and I'm not getting any hits. Am I doing something wrong, or is there a better way to approach this task?

Thanks in advance.

Ideally you would have the color on a binding.

You might be able to script it. But it will tricky as you will have to loop through every page and every component for that property.

Here is a recursive script that would probably get quite a bit of the work done on a per window basis:

Prototype Function
from java.awt import Color
from javax.swing.plaf import ColorUIResource
findRGB = [0, 0, 0]
replaceRGB = [255, 255, 255]
def colorFinder(component):
	for component in component.getComponents():
		if component.background == Color(r, g, b) or component.background == ColorUIResource(r, g, b):
			component.background = Color(newR, newG, newB)
		if component.foreground == Color(r, g, b) or component.foreground == ColorUIResource(r, g, b):
			component.foreground = Color(newR, newG, newB)
		colorFinder(component)
r = findRGB[0]
g = findRGB[1]
b = findRGB[2]
newR = replaceRGB[0]
newG = replaceRGB[1]
newB = replaceRGB[2]
parentWindow = system.gui.getParentWindow(event)
colorFinder(parentWindow)

Edit: While the prototype function I initially developed as a solution to this problem does work, it is inefficient. Instead, use Paul Griffith's rewrite below.

The script finds the parent window, and then recursively loops through every subcomponent looking for the foreground and background properties. It compares those properties to the findRGB list in line three of the script, and if they match, it replaces them with a java.awt.Color defined by the replaceRGB list in line four of the script.

If looping through all the windows is needed, wrap the preceding script in the following code:

for name in system.gui.getWindowNames():
	system.nav.openWindow(name)
	#[put the above listed script here]
	system.nav.closeWindow(name)

If there are hundreds of windows, then consider defining a range to limit the for loop, and conduct the update in batches.

1 Like

Addressing the issue here would be the better option, where the issue is that magic colours have presumably been used throughout the project, instead of being bound to a single source of truth. For example, colours (HEX) stored inside of a client tag(s) (individual or in datasets).

If this were the case, then this change is simple: make the few changes in the source colours, and then job done. It also means that anything new doesn't have to refer to another component to get the colours right, they're named appropriately at the source and can simply be picked. If you look at my posts in the topic below as well, if you use a dataset tag and a lookup function, you don't even have to pick a source colour - the lookup simply looks up the colour for you (although I don't have a solution for blinking colours using the dataset method at the moment - I haven't really thought much about it since we're now using Perspective). For individual colour tags, I used to use:

case({<Device Status Tag Path>}
	,'Stopped', {[Client]System/Styles/Colours/Device/Off}
	,'Running', {[Client]System/Styles/Colours/Device/On}
	,'Faulted', if({[Client]System/Util/Clock_1s}, {[Client]System/Styles/Colours/Device/Faulted}, {[Client]System/Styles/Colours/Device/Faulted_Flash})
	,'Invalid', {[Client]System/Styles/Colours/Device/INVALID}
)

Obviously, Perspective does this far more superiorly to Vision with its Perspective Styles, but you can at least have a basic version of this in Vision.

Have a read of this topic for more ideas:

2 Likes

Thank you for the suggestion, Nick. That's the dream, were it not for being a legacy project. :upside_down_face: Hopefully we can try to implement some similar standards when we complete this recolor.

Awesome; thank you Justin. I'll give this a shot on a single window and see where we get. I appreciate the example script.

I rewrote @justinedwards.jle's example with an eye for performance; basically avoiding a lot of unnecessary allocations of new Color objects:

findColor = system.gui.color(0, 0, 0)
replaceColor = system.gui.color(255, 255, 255)

def colorFinder(component):
	for component in component.getComponents():
		if component.background.rgb == findColor.rgb
			component.background = replaceColor
		if component.foreground.rgb == findColor.rgb
			component.foreground = replaceColor
		colorFinder(component)

parentWindow = system.gui.getParentWindow(event)
colorFinder(parentWindow)
4 Likes

Thank you Paul & co for all of your assistance. Looks like this will do the trick.

1 Like

although I don't have a solution for blinking colours using the dataset method at the moment

if({[client]System/1s}=true,
	lookup({[client]System/Styles/Colors/Styles}, 
		"screenBackground", "",  "componentProperty", "color"),
	lookup({[client]System/Styles/Colors/Styles}, 
		"screenBackgroundFlash", "", "componentProperty", "color"))

Is there a reason not to something like this?