qupflowR is being built to connect R to QuPath 0.7.0 through a local Java bridge, with a Shiny app on top of the same R functions and shareable analysis workflows. This vignette describes what the current development version can and cannot do. It is updated whenever a new part becomes usable.
This version is the foundation of the package. It has no functions for QuPath projects, images or objects yet.
| Part | State in this version |
|---|---|
| Package skeleton, tests and quality checks | first version |
| Classified error conditions and the error code registry | first version |
| Message catalogue in German and English | first version |
| JSON helpers and JSON Schema validation for the bridge protocol | first version, building blocks for the bridge client |
| Pinned execution of an explicitly trusted external method | implemented for Linux; separate from the live bridge |
| Java bridge (QuPath extension and headless worker) | in development, not reachable from R |
| Connecting to QuPath, reading projects, objects and measurements | planned |
| Shiny app | planned |
| Shareable workflows | planned |
Loading the package is safe on any machine:
library(qupflowR) starts no Java, QuPath, Python or browser
process, downloads nothing and does not load optional packages such as
shiny, sf or hyperannotatR.
Every error that qupflowR raises has a code from one registry. The Java bridge reads the same registry, so an error reported by the bridge and the matching R condition agree on the code, the R condition class, the HTTP status and whether a retry is safe.
codes <- qp_error_codes()
codes[codes$code %in% c("HOST_BUSY", "REVISION_CONFLICT"),
c("code", "http_status", "r_class", "retryable")]
#> # A tibble: 2 × 4
#> code http_status r_class retryable
#> <chr> <int> <chr> <lgl>
#> 1 REVISION_CONFLICT 409 qp_conflict_error FALSE
#> 2 HOST_BUSY 503 qp_busy_error TRUECode that calls qupflowR can catch a class of problems, or look at the code:
User-facing messages come from a catalogue. German is the default;
the option qupflowR.language selects English.
The Shiny app, geometry conversion with sf and the hyperannotatR
interoperability will need packages that are listed in
Suggests. They are checked only where a feature uses them.
A missing package then produces a qp_capability_error with
the code CAPABILITY_UNAVAILABLE that names the packages to
install.
Protocol IDs are character values. Mark a scalar explicitly; unmarked vectors remain arrays, even when they contain only one element.
payload <- list(
image_id = qp_scalar("synthetic-image"),
object_ids = c("1", "2"),
metadata = qp_empty_object()
)
encoded <- qp_json_encode(payload)
encoded
#> {"image_id":"synthetic-image","object_ids":["1","2"],"metadata":{}}
qp_json_decode(encoded)$object_ids
#> [1] "1" "2"
qp_value_state(c(1.5, NA_real_, NaN, Inf, -Inf))
#> [1] "finite" "missing" "nan" "pos_inf" "neg_inf"The schema validator checks protocol structure, not the existence of a running bridge or the correctness of image measurements. An incomplete health document therefore produces a report with errors:
report <- qp_schema_validate(qp_as_json("{}"), "health")
report$valid
#> [1] FALSE
head(report$errors)
#> # A tibble: 6 × 4
#> path keyword message schema_path
#> <chr> <chr> <chr> <chr>
#> 1 "" oneOf Genau eine Alternative muss passen; passend: 0, 1. health.sche…
#> 2 "" required Die Pflichteigenschaft `status` fehlt. health.sche…
#> 3 "" required Die Pflichteigenschaft `host_type` fehlt. health.sche…
#> 4 "" required Die Pflichteigenschaft `qupath_version` fehlt. health.sche…
#> 5 "" required Die Pflichteigenschaft `bridge_version` fehlt. health.sche…
#> 6 "" required Die Pflichteigenschaft `protocol_version` fehlt. health.sche…Nothing has been released yet. Planned functions such as connecting to a running QuPath or opening a project do not exist in this version, so this vignette does not show them.
qp_run_external() is a separate compatibility boundary
for an explicitly trusted, hash-pinned executable and argument vector.
It leaves analytical scripts and runtime choices unchanged. Declare all
script, runtime, model and input pins; use a new workspace and explicit
relative output files. The {{workspace}} argument token
expands without a shell. A bounded private process environment prevents
accidental use of the user’s desktop/preferences.
The method itself must validate its analytical outputs. The returned
execution_complete status records process success,
unchanged declared inputs and nonempty output artifacts; it does not
imply scientific equivalence or a successful project save. Keep study
configuration and data outside the public package. This additive API is
not the future queued workflow engine and does not qualify the
developing live bridge for other QuPath versions.
The initial execution profile is Linux-only. Its receipt records the complete explicit child environment, and Java property values retain paths containing spaces or quotes. Other platforms remain unavailable until separately qualified.
The following Linux example creates a synthetic method in a temporary
directory, pins the executable and script, and checks the output against
a known value. It does not read study files, start QuPath or access the
network. digest, processx and fs
are optional dependencies required by this API. Install them with
install.packages(c("digest", "processx", "fs")) before
using it. The example is evaluated on Linux when all three are
installed; elsewhere it is shown as code only. Real methods must also
pin their data, runtime and model dependencies.
local({
example_dir <- tempfile("qupflowr-example-")
dir.create(example_dir)
on.exit(unlink(example_dir, recursive = TRUE), add = TRUE)
script <- file.path(example_dir, "synthetic-method.R")
writeLines(c(
"out <- commandArgs(trailingOnly = TRUE)[1]",
"writeLines('42', file.path(out, 'result.txt'))"
), script)
program <- normalizePath(file.path(R.home("bin"), "Rscript"))
paths <- c(program, script)
pins <- data.frame(
path = paths,
sha256 = vapply(paths, function(path) {
digest::digest(file = path, algo = "sha256")
}, character(1), USE.NAMES = FALSE)
)
result <- qp_run_external(
program = program,
args = c("--vanilla", script, "{{workspace}}"),
pins = pins,
outputs = "result.txt",
workspace = file.path(example_dir, "run"),
timeout = 30,
trust = TRUE
)
observed <- readLines(file.path(result$workspace, "result.txt"))
if (!identical(observed, "42")) {
cli::cli_abort("Unexpected synthetic output.", class = "qp_example_error")
}
result$status
})
#> [1] "execution_complete"An existing workspace, a changed pinned file, a nonzero process exit,
or a missing/empty declared output fails the run. Inspect the retained
run.json and process logs for real failures. Keep those
receipts private because they record paths and the explicit child
environment.
Use help(package = "qupflowR") for the function
reference and citation("qupflowR") for citation details.
The package is GPL (>= 3). Report reproducible issues at https://github.com/CTTIR/qupflowR/issues.