Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify extension to request raw tiles when possible #1

Merged
merged 26 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1fd1c91
Work in progress to support raw tiles
melissalinkert Nov 2, 2022
98498d4
Fix the port used to connect to ms-pixel-buffer
melissalinkert Dec 15, 2022
3e2fbc3
Connect to microservice with http instead of https
melissalinkert Dec 15, 2022
e998d95
Copy channel names and colors from OMERO
melissalinkert Dec 17, 2022
cb0d553
Fix tile XY coordinates
melissalinkert Dec 17, 2022
00b4a0c
Fix resolution indexing
melissalinkert Dec 19, 2022
c2983f3
Save server URLs so they don't need to be typed every time
melissalinkert Jan 6, 2023
201bda8
Make microservice optional, fall back to webgateway if not available
melissalinkert Apr 11, 2023
bea3cd8
Add some clarifying comments and error handling
melissalinkert Apr 14, 2023
2e6486c
Fix microservice check
melissalinkert May 30, 2023
238c122
Check for pixelbuffer microservice instead of imageregion
melissalinkert Jun 2, 2023
e2a5262
Update gradle workflow to retain artifact
melissalinkert Nov 10, 2023
294bae1
Use repository that includes cisd:jhdf:19.04.0
melissalinkert Mar 6, 2024
b5f83bf
Reintroduce scijava releases repo so that qupath-gui-fx is found
melissalinkert Mar 6, 2024
f0dd726
Fix path of artifacts to upload
melissalinkert Mar 6, 2024
2135dfe
One more artifact path fix
melissalinkert Mar 6, 2024
c7e025c
Update dependencies and fix build for compatibility with Java 17 / Qu…
melissalinkert Mar 12, 2024
4e819fe
Fix server list property parsing
melissalinkert Mar 13, 2024
a1da8fc
Always use a new CookieManager instead of the default
melissalinkert Mar 13, 2024
660edd4
Fix a few more issues with server list property handling
melissalinkert Mar 13, 2024
5d64b83
Handle trailing / in saved server names
melissalinkert Mar 14, 2024
0095d5a
Update version number to 0.4.1-gs-SNAPSHOT
melissalinkert Mar 22, 2024
b178bd2
Update README to clarify what this fork does
melissalinkert Mar 22, 2024
92e8610
Merge branch 'main' of github.com:glencoesoftware/qupath-extension-om…
melissalinkert Mar 22, 2024
4f66ae2
Merge branch 'main' of github.com:glencoesoftware/qupath-extension-om…
melissalinkert Mar 26, 2024
eedc8c5
Fix GitHub link reported in extension manager
melissalinkert Mar 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/build_jar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '17'
distribution: 'adopt-hotspot'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build -P toolchain=11
- uses: actions/upload-artifact@v2
run: ./gradlew build -P toolchain=17
- uses: actions/upload-artifact@v4
with:
name: libs
path: build/libs
Expand Down
40 changes: 24 additions & 16 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
# This workflow will build a Java project with Gradle
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle

name: Java CI with Gradle
name: Java CI with Gradle

on: [pull_request]
on:
push:
pull_request:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt-hotspot'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build -P toolchain=11
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'adopt-hotspot'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build -P toolchain=17
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: qupath-extension-omero-gs
path: build/libs/*.jar
retention-days: 30
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
[![Extension docs](https://img.shields.io/badge/docs-qupath_omero-red)](https://qupath.readthedocs.io/en/stable/docs/advanced/omero.html)
[![Forum](https://img.shields.io/badge/forum-image.sc-green)](https://forum.image.sc/tag/qupath)
[![Downloads (latest release)](https://img.shields.io/github/downloads-pre/qupath/qupath-extension-omero/latest/total)](https://github.com/qupath/qupath-extension-omero/releases/latest)
[![Downloads (all releases)](https://img.shields.io/github/downloads/qupath/qupath-extension-omero/total)](https://github.com/qupath/qupath-extension-omero/releases)
# QuPath OMERO extension with raw tiles support

# QuPath OMERO extension

Welcome to the OMERO extension for [QuPath](http://qupath.github.io)!
Welcome to the Glencoe Software's version of the OMERO extension for [QuPath](http://qupath.github.io)!

This adds support for accessing images hosted on an [OMERO](https://www.openmicroscopy.org/omero/)
server through OMERO's web API.
server. If microservices are available on the OMERO server, then they will be used to retrieve
image data; all data types are supported in this case, and uncompressed image data is provided to QuPath.

If microservices are not available, then the OMERO web API is used. In this case, only uint8 images
with 3 or fewer channels are supported.

> **Important!**
>
Expand All @@ -29,7 +28,7 @@ The main documentation for the extension is at https://qupath.readthedocs.io/en/

## Installing

To install the OMERO extension, download the latest `qupath-extension-omero-[version].jar` file from [releases](https://github.com/qupath/qupath-extension-omero/releases) and drag it onto the main QuPath window.
To install the OMERO extension, download the latest `qupath-extension-omero-[version].jar` file from [releases](https://github.com/glencoesoftware/qupath-extension-omero/releases) and drag it onto the main QuPath window.

If you haven't installed any extensions before, you'll be prompted to select a QuPath user directory.
The extension will then be copied to a location inside that directory.
Expand Down
49 changes: 31 additions & 18 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,36 @@ plugins {
}

ext.moduleName = 'qupath.extension.omero'
ext.qupathVersion = gradle.ext.qupathVersion
ext.qupathJavaVersion = 17

base {
description = "QuPath extension to support image reading using OMERO's web API."
version = "0.4.0"
group = "io.github.qupath"
}
archivesBaseName = 'qupath-extension-omero'
description = "QuPath extension to support image reading using OMERO's web API."
version = "0.4.1-gs-SNAPSHOT"

repositories {

mavenLocal()
mavenCentral()

maven {
url "https://maven.scijava.org/content/repositories/releases"
url 'https://artifacts.glencoesoftware.com/artifactory/scijava-thirdparty'
}

maven {
url "https://maven.scijava.org/content/repositories/snapshots"
url 'https://artifacts.glencoesoftware.com/artifactory/ome.releases'
}


maven {
url "https://maven.scijava.org/content/repositories/releases"
}

}

dependencies {
implementation libs.commons.text
shadow "io.github.qupath:qupath-gui-fx:${qupathVersion}"
shadow libs.qupath.fxtras

shadow libs.slf4j
implementation "org.apache.commons:commons-text:1.9"

shadow "io.github.qupath:qupath-gui-fx:0.5.1"
shadow "io.github.qupath:qupath-fxtras:0.1.4"
shadow "ome:formats-bsd:7.0.1"
shadow "org.slf4j:slf4j-api:1.7.30"
}

jar {
Expand Down Expand Up @@ -88,3 +89,15 @@ tasks.withType(org.gradle.jvm.tasks.Jar) {
tasks.named('test') {
useJUnitPlatform()
}

javafx {
version = "17.0.9"
modules = ["javafx.base",
"javafx.controls",
"javafx.graphics",
"javafx.media",
"javafx.fxml",
"javafx.web",
"javafx.swing"]
configuration = 'api'
}
131 changes: 93 additions & 38 deletions src/main/java/qupath/lib/images/servers/omero/OmeroExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,19 @@
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;

import javafx.beans.property.StringProperty;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.control.Label;
Expand All @@ -46,6 +53,7 @@
import qupath.lib.gui.actions.ActionTools;
import qupath.lib.gui.extensions.GitHubProject;
import qupath.lib.gui.extensions.QuPathExtension;
import qupath.lib.gui.prefs.PathPrefs;
import qupath.lib.gui.tools.MenuTools;

/**
Expand Down Expand Up @@ -102,16 +110,23 @@ public String getDescription() {

private static Menu createServerListMenu(QuPathGUI qupath, Menu browseServerMenu) {
EventHandler<Event> validationHandler = e -> {
StringProperty usedServerProp = PathPrefs.createPersistentPreference("omero_ext.server_list", "");

List<String> usedServers = getServerList(usedServerProp);

browseServerMenu.getItems().clear();

// Get all active servers
var activeServers = OmeroWebClients.getAllClients();

// Populate the menu with each unique active servers
ArrayList<String> activeURIs = new ArrayList<String>();
for (var client: activeServers) {
if (client == null)
continue;
// 3 dots are appended to distinguish active connections
MenuItem item = new MenuItem(client.getServerURI() + "...");
activeURIs.add(client.getServerURI().toString());
item.setOnAction(e2 -> {
var browser = browsers.get(client);
if (browser == null || browser.getStage() == null) {
Expand All @@ -123,6 +138,23 @@ private static Menu createServerListMenu(QuPathGUI qupath, Menu browseServerMenu
});
browseServerMenu.getItems().add(item);
}

// add servers that have been connected to previously,
// but which are not currently connected
if (usedServers != null) {
for (String server : usedServers) {
final String usedServer = server.endsWith("/") ? server.substring(0, server.length() - 1) : server;
if (!usedServer.isEmpty() && !activeURIs.contains(usedServer)) {
// no suffix appended to the server name here
// distinguishes from active connections that have 3 dots appended
MenuItem item = new MenuItem(usedServer);
item.setOnAction(e2 -> {
handleLogin(qupath, usedServer);
});
browseServerMenu.getItems().add(item);
}
}
}

// Create 'New server...' MenuItem
MenuItem customServerItem = new MenuItem("New server...");
Expand All @@ -140,50 +172,73 @@ private static Menu createServerListMenu(QuPathGUI qupath, Menu browseServerMenu
var path = tf.getText();
if (path == null || path.isEmpty())
return;
try {
if (!path.startsWith("http:") && !path.startsWith("https:"))
throw new IOException("The input URL must contain a scheme (e.g. \"https://\")!");

// Make the path a URI
URI uri = new URI(path);

// Clean the URI (in case it's a full path)
URI uriServer = OmeroTools.getServerURI(uri);

if (uriServer == null)
throw new MalformedURLException("Could not parse server from " + uri.toString());

// Check if client exist and if browser is already opened
var client = OmeroWebClients.getClientFromServerURI(uriServer);
if (client == null)
client = OmeroWebClients.createClientAndLogin(uriServer);

if (client == null)
throw new IOException("Could not parse server from " + uri.toString());

var browser = browsers.get(client);
if (browser == null || browser.getStage() == null) {
// Create new browser
browser = new OmeroWebImageServerBrowserCommand(qupath, client);
browsers.put(client, browser);
browser.run();
} else // Request focus for already-existing browser
browser.getStage().requestFocus();

} catch (FileNotFoundException ex) {
Dialogs.showErrorMessage("OMERO web server", String.format("An error occured when trying to reach %s: %s\"", path, ex.getLocalizedMessage()));
} catch (IOException | URISyntaxException ex) {
Dialogs.showErrorMessage("OMERO web server", ex.getLocalizedMessage());
return;
}

handleLogin(qupath, path);

List<String> serverList = getServerList(usedServerProp);
if (serverList != null && !serverList.contains(path)) {
serverList.add(path);
usedServerProp.setValue((new Gson()).toJson(serverList));
}
});
MenuTools.addMenuItems(browseServerMenu, null, customServerItem);
};

// Ensure the menu is populated (every time the parent menu is opened)
browseServerMenu.getParentMenu().setOnShowing(validationHandler);
return browseServerMenu;
}

private static List<String> getServerList(StringProperty usedServerProp) {
Gson gson = new Gson();
List<String> usedServers = null;
try {
usedServers = gson.fromJson(usedServerProp.get(), new TypeToken<>() {});
} catch (JsonSyntaxException ignored) {}
if (usedServers == null) {
usedServers = new ArrayList<String>();
}
return usedServers;
}

static void handleLogin(QuPathGUI qupath, String path) {
try {
if (!path.startsWith("http:") && !path.startsWith("https:")) {
throw new IOException("The input URL must contain a scheme (e.g. \"https://\")!");
}
// Make the path a URI
URI uri = new URI(path);

// Clean the URI (in case it's a full path)
URI uriServer = OmeroTools.getServerURI(uri);

if (uriServer == null)
throw new MalformedURLException("Could not parse server from " + uri.toString());

// Check if client exist and if browser is already opened
var client = OmeroWebClients.getClientFromServerURI(uriServer);
if (client == null)
client = OmeroWebClients.createClientAndLogin(uriServer);

if (client == null)
throw new IOException("Could not parse server from " + uri.toString());

var browser = browsers.get(client);
if (browser == null || browser.getStage() == null) {
// Create new browser
browser = new OmeroWebImageServerBrowserCommand(qupath, client);
browsers.put(client, browser);
browser.run();
} else // Request focus for already-existing browser
browser.getStage().requestFocus();

} catch (FileNotFoundException ex) {
Dialogs.showErrorMessage("OMERO web server", String.format("An error occured when trying to reach %s: %s\"", path, ex.getLocalizedMessage()));
} catch (IOException | URISyntaxException ex) {
Dialogs.showErrorMessage("OMERO web server", ex.getLocalizedMessage());
return;
}
}

/**
* Return map of currently opened browsers.
Expand All @@ -197,7 +252,7 @@ static Map<OmeroWebClient, OmeroWebImageServerBrowserCommand> getOpenedBrowsers(

@Override
public GitHubRepo getRepository() {
return GitHubRepo.create(getName(), "qupath", "qupath-extension-omero");
return GitHubRepo.create(getName(), "glencoesoftware", "qupath-extension-omero-web");
}

@Override
Expand Down
Loading