Calling setChannelNames after setImageData is not refreshing channel names


I have what is probably a dumb question coming from someone with little to no java experience (I’ve mostly worked in C,C++ along with Lua and some Python). I am trying to make a simple script to allow a user to select one or more images (ome tiffs) that can then be loaded up as a multi-channel image in QuPath (which is an amazingly awesome piece of sofware, thanks!).

I have cribbed together some code from the very helpful examples provided in this forum, and think I am getting close (code pasted below), but am running in to a channel naming issue.I am trying to set some semi-resonable channel names, so for now am just trying to use the actual filenames. The issue I am running in to is that if I call setChannelNames() after setImageData (in the Platform.runLater call) I can’t get the channel names to take effect. If I skip setImageData I can get the channel names, but of course then don’t have the images loaded.

I’m sure I am doing something dumb with regards to the runLater call (interacts with the GUI thread, right?), but am now sure what I need to do to get the channel names to stick. I recall seeing a comment

This java novice would greatly appreciate any help :slight_smile:



import javafx.application.Platform
import qupath.lib.images.ImageData
import qupath.lib.images.servers.ConcatChannelsImageServer
import qupath.lib.images.servers.ImageServerProvider

import java.awt.image.BufferedImage

import static qupath.lib.gui.scripting.QPEx.*

boolean promptForFiles = true

File dir
List<File> files
String baseName = 'Merged image'
if (promptForFiles) {
    def qupath = getQuPath()
    files = Dialogs.promptForMultipleFiles("Choose input files", null, "TIFF files", ".tif", ".tiff")
} else {
    // Try to get the URI of the current image that is open
    def currentFile = new File(getCurrentServer().getURIs()[0])
    dir = currentFile.getParentFile()
    // This naming scheme works for me...
    String name = currentFile.getName()
    int ind = name.indexOf("_[")
    if (ind < 0)
        ind = name.toLowerCase().lastIndexOf('.tif')
    if (ind >= 0)
        baseName = currentFile.getName().substring(0, ind)
    // Get all the non-OME TIFF files in the same directory
    files = dir.listFiles().findAll {
        return it.isFile() &&
                !it.getName().endsWith('.ome.tif') &&
                (baseName == null || it.getName().startsWith(baseName))
        (it.getName().endsWith('.tiff') || it.getName().endsWith('.tif') || checkTIFF(file))
if (!files) {
    print 'No TIFF files selected'

String[] paths = new String[files.size()];
for (int i = 0; i < files.size(); i++) {
   paths[i] = files[i];

// set the channel names
String[] chan_names = new String[files.size()];
for (int i = 0; i < files.size(); i++) {
   chan_names[i] = files[i].getName();


// Build servers for each channel (could be faster if we know which kind of server we need)
def channelServers = paths.collect {
    return ImageServerProvider.buildServer(it, BufferedImage)
print channelServers

// Create a concatenated server
def server = new ConcatChannelsImageServer(channelServers[0], channelServers)

// Show the image
def imageData = new ImageData(server)
// because of the runLater, this happens after we set the channel names
Platform.runLater {
    print("done loading")

print("time to set channel names")
// unfortunately this setChannelNames doesn't do anything unless we get rid of the Platform.runLater stuff above
// in which case the new images don't actually show up

Welcome @adcorwin and thanks for the nice words about QuPath :slight_smile:

What I suspect is happening is that the final line is run before Platform.runLater (in another thread)… which has the effect that the current image within the script at the time setChannelNames is called isn’t the one you are in the process of adding.

With the caveat that I haven’t tried it myself, you could try this:

Platform.runLater {
    setChannelNames(imageData, chan_names)
    print("done loading")

Or otherwise use the version of setChannelNames that takes an ImageData as its first argument called before Platform.runLater – since then QuPath won’t try (and fail) to figure out the ‘current’ image it should be setting the names for.

Hi Peter,

Thank you so much for the quick response! That totally did the trick (calling SetChanelNames directly after the getCurrentViewer() command). I had tried this previously but hadn’t included the imageData as the first argument to setChannelNames.