Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelSNelson authored Nov 27, 2023
0 parents commit 2dccbda
Show file tree
Hide file tree
Showing 13 changed files with 874 additions and 0 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle

name: Java CI with Gradle

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
- name: Build with Gradle
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
with:
arguments: build
- uses: actions/upload-artifact@v3
with:
name: jar
path: build/libs
retention-days: 7
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Javadocs
docs/

# Maven
deploy/
target/
log/

# IntelliJ
.idea/
*.iml
out/

# Gradle
# Use local properties (e.g. to set a specific JDK)
gradle.properties
build/
.gradle/
gradle.properties

# Eclipse
.settings/
.project
.classpath

# VSCode
.vscode/

# Mac
.DS_Store

# Java
hs_err*.log

# Other
*.tmp
*.bak
*.swp
*~.nib
*thumbs.db
bin/
115 changes: 115 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# QuPath extension template

This repo contains a template and instructions to help create a new extension for [QuPath](https://qupath.github.io).

It already contains two minimal extensions, so the first task is to make sure that they work.
Then, it's a matter of customizing the code to make it more useful.

> There are two extensions to show that you can use either Java or Groovy.
## Build the extension

Building the extension with Gradle should be pretty easy - you don't even need to install Gradle separately, because the
[Gradle Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html) will take care of that.

Open a command prompt, navigate to where the code lives, and use
```bash
gradlew build
```

The built extension should be found inside `build/libs`.
You can drag this onto QuPath to install it.
You'll be prompted to create a user directory if you don't already have one.

The minimal extension here doesn't do much, but it should at least install a new command under the 'Extensions' menu in
QuPath.

> In case your extension contains external dependencies beyond what QuPath already includes, you can create a
> [single jar file](https://imperceptiblethoughts.com/shadow/introduction/#benefits-of-shadow) that bundles these along
> with your extension by using
> ```bash
> gradlew shadowJar
> ```
> If you don't do that, you'll need to drag *all* the extra dependences onto QuPath to install them as well.
## Set up in an IDE (optional)
During development, things are likely to be much easier if you work within an IDE.
QuPath itself is developed using IntelliJ, and you can import the extension template there.
However, for development and testing, it can help to import QuPath *and* the extension and have them in your IDE side-by-side.
In IntelliJ, you can do this in a few steps:
* Get QuPath's source code, as described at https://qupath.readthedocs.io/en/0.4/docs/reference/building.html
* Store your extension code in a directory *beside* QuPath's code. So it should be located next to the `qupath` code directory.
* Import QuPath into IntelliJ as a Gradle project (you don't need to import the extension yet!)
* See https://www.jetbrains.com/help/idea/work-with-gradle-projects.html
* Within `qupath/settings.gradle` add the line `includeFlat 'your-extension-code-directory'` (updating the code directory as needed)
* Refresh the Gradle project in IntelliJ, and your extension code should appear
* Create a [Run configuration](https://www.jetbrains.com/help/idea/run-debug-configuration.html) in IntelliJ to launch QuPath. An example of how that looks is shown below:
<img src="qupath-intellij.png" alt="QuPath run configuration in IntelliJ" width="428" />
Now when you run QuPath from IntelliJ, your extension should (hopefully) be found - there's no need to add it by drag & drop.
## Customize the extension
There are a few fixed steps to customizing the extension, and then the main creative part where you add your own code.
### Update `settings.gradle`
Open `settings.gradle` and check the comment lines flagged with `\\TODO`.
These point you towards parts you may well need to change.
### Update `build.gradle`
Open `build.gradle` and follow a similar process to with `settings.gradle`, to update the bits flagged with `\\TODO`.
### Create the extension Java or Groovy file(s)
For the extension to work, you need to create at least one file that extends `qupath.lib.gui.extensions.QuPathExtension`.
There are two examples in the template, in two languages:
* **Java:** `qupath.ext.template.DemoExtension.java`.
* **Groovy:** `qupath.ext.template.DemoGroovyExtension.java`.
You can pick the one that corresponds to the language you want to use, and delete the other.
Then take your chosen file and rename it, edit it, move it to another package... basically, make it your own.
> Please **don't neglect this step!**
> If you do, there's a chance of multiple extensions being created with the same class names... and causing confusion later.
### Update the `META-INF/services` file
For QuPath to *find* the extension later, the full class name needs to be available in `resources/META-INFO/services/qupath.lib.gui.extensions.QuPathExtensions`.
So remember to edit that file to include the class name that you actually used for your extension.
### Specify your license
Add a license file to your GitHub repo so that others know what they can and can't do with your extension.
This should be compatible with QuPath's license -- see https://github.com/qupath/qupath
### Replace this readme
Don't forget to replace the contents of this readme with your own!
## Getting help
For questions about QuPath and/or creating new extensions, please use the forum at https://forum.image.sc/tag/qupath
------
## License
This is just a template, you're free to use it however you like.
You can treat the contents of *this repository only* as being under [the Unlicense](https://unlicense.org) (except for the Gradle wrapper, which has its own license included).
If you use it to create a new QuPath extension, I'd strongly encourage you to select a suitable open-source license for the extension.
Note that *QuPath itself* is available under the GPL, so you do have to abide by those terms: see https://github.com/qupath/qupath for more.
151 changes: 151 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
plugins {
// Main gradle plugin for building a Java library
id 'java-library'
// Support writing the extension in Groovy (remove this if you don't want to)
id 'groovy'
// To create a shadow/fat jar that bundle up all dependencies
id 'com.github.johnrengelman.shadow' version '7.1.2'
// Include this plugin to avoid downloading JavaCPP dependencies for all platforms
id 'org.bytedeco.gradle-javacpp-platform'
}

// TODO: Change the module name
ext.moduleName = 'io.github.qupath.extension.template'

// TODO: Define the extension version & provide a short description
version = "0.1.0-SNAPSHOT"
description = 'A simple QuPath extension template'

// TODO: Specify the QuPath version, compatible with the extension.
// The default 'gradle.ext.qupathVersion' reads this from settings.gradle.
ext.qupathVersion = gradle.ext.qupathVersion

// TODO: Specify the Java version compatible with the extension
// Generally 11 for QuPath v0.4.3, but will be 17 for QuPath v0.5.0
ext.qupathJavaVersion = 11

/**
* Define dependencies.
* - Using 'shadow' indicates that they are already part of QuPath, so you don't need
* to include them in your extension. If creating a single 'shadow jar' containing your
* extension and all dependencies, these won't be added.
* - Using 'implementation' indicates that you need the dependency for the extension to work,
* and it isn't part of QuPath already. If you are creating a single 'shadow jar', the
* dependency should be bundled up in the extension.
* - Using 'testImplementation' indicates that the dependency is only needed for testing,
* but shouldn't be bundled up for use in the extension.
*/
dependencies {

// Main QuPath user interface jar.
// Automatically includes other QuPath jars as subdependencies.
shadow "io.github.qupath:qupath-gui-fx:${qupathVersion}"

// For logging - the version comes from QuPath's version catalog at
// https://github.com/qupath/qupath/blob/main/gradle/libs.versions.toml
// See https://docs.gradle.org/current/userguide/platforms.html
shadow libs.slf4j

// If you aren't using Groovy, this can be removed
shadow libs.bundles.groovy

testImplementation "io.github.qupath:qupath-gui-fx:${qupathVersion}"
testImplementation libs.junit
}


/*
* Manifest info
*/
jar {
manifest {
attributes("Implementation-Title": project.name,
"Implementation-Version": archiveVersion,
"Automatic-Module-Name": moduleName)
}
}

/*
* Copy the LICENSE file into the jar... if we have one (we should!)
*/
processResources {
from ("${projectDir}/LICENSE") {
into 'licenses/'
}
}

/*
* Define extra 'copyDependencies' task to copy dependencies into the build directory.
*/
tasks.register("copyDependencies", Copy) {
description "Copy dependencies into the build directory for use elsewhere"
group "QuPath"

from configurations.default
into 'build/libs'
}

/*
* Ensure Java 11 compatibility, and include sources and javadocs when building.
*/
java {
toolchain {
languageVersion = JavaLanguageVersion.of(qupathJavaVersion)
}
withSourcesJar()
withJavadocJar()
}

/*
* Create javadocs for all modules/packages in one place.
* Use -PstrictJavadoc=true to fail on error with doclint (which is rather strict).
*/
tasks.withType(Javadoc) {
options.encoding = 'UTF-8'
def strictJavadoc = findProperty('strictJavadoc')
if (!strictJavadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}

/*
* Specify that the encoding should be UTF-8 for source files
*/
tasks.named('compileJava') {
options.encoding = 'UTF-8'
}

/*
* Avoid 'Entry .gitkeep is a duplicate but no duplicate handling strategy has been set.'
* when using withSourcesJar()
*/
tasks.withType(org.gradle.jvm.tasks.Jar) {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}

/*
* Support tests with JUnit.
*/
tasks.named('test') {
useJUnitPlatform()
}

// Looks redundant to include this here and in settings.gradle,
// but helps overcome some gradle trouble when including this as a subproject
// within QuPath itself (which is useful during development).
repositories {
// Add this if you need access to dependencies only installed locally
// mavenLocal()

mavenCentral()

// Add scijava - which is where QuPath's jars are hosted
maven {
url "https://maven.scijava.org/content/repositories/releases"
}

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

}
Binary file added gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
5 changes: 5 additions & 0 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit 2dccbda

Please sign in to comment.