Combine xlarge width with updated heading
This commit is contained in:
commit
931554226e
22 changed files with 150 additions and 217 deletions
|
|
@ -7,12 +7,7 @@
|
|||
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
|
||||
"/system_control/": "openflexure_microscope_server.things.system_control:SystemControlThing",
|
||||
"/settings/": "openflexure_microscope_server.things.settings_manager:SettingsManager",
|
||||
"/smart_scan/": {
|
||||
"class": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
|
||||
"kwargs": {
|
||||
"path_to_openflexure_stitch": "application/openflexure-stitching/.venv/bin/openflexure-stitch"
|
||||
}
|
||||
},
|
||||
"/smart_scan/": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
|
||||
"/background_detect/": "openflexure_microscope_server.things.background_detect:BackgroundDetectThing",
|
||||
"/capture/": "openflexure_microscope_server.things.capture:CaptureThing"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,12 +7,7 @@
|
|||
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
|
||||
"/system_control/": "openflexure_microscope_server.things.system_control:SystemControlThing",
|
||||
"/settings/": "openflexure_microscope_server.things.settings_manager:SettingsManager",
|
||||
"/smart_scan/": {
|
||||
"class": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
|
||||
"kwargs": {
|
||||
"path_to_openflexure_stitch": "application/openflexure-stitching/.venv/bin/openflexure-stitch"
|
||||
}
|
||||
},
|
||||
"/smart_scan/": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
|
||||
"/background_detect/": "openflexure_microscope_server.things.smart_scan:BackgroundDetectThing",
|
||||
"/api_test/": "openflexure_microscope_server.things.test:APITestThing"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,12 +7,7 @@
|
|||
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
|
||||
"/system_control/": "openflexure_microscope_server.things.system_control:SystemControlThing",
|
||||
"/settings/": "openflexure_microscope_server.things.settings_manager:SettingsManager",
|
||||
"/smart_scan/": {
|
||||
"class": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
|
||||
"kwargs": {
|
||||
"path_to_openflexure_stitch": "application/openflexure-stitching/.venv/bin/openflexure-stitch"
|
||||
}
|
||||
},
|
||||
"/smart_scan/": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
|
||||
"/background_detect/": "openflexure_microscope_server.things.smart_scan:BackgroundDetectThing",
|
||||
"/api_test/": "openflexure_microscope_server.things.test:APITestThing"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ dependencies = [
|
|||
"labthings-sangaboard",
|
||||
"camera-stage-mapping ~= 0.1.10",
|
||||
"opencv-python ~= 4.11.0",
|
||||
"openflexure-stitching[libvips]==0.1.0",
|
||||
"pillow ~= 10.4",
|
||||
"anyio ~= 4.0",
|
||||
"numpy ~= 2.2",
|
||||
|
|
|
|||
|
|
@ -1,41 +1,39 @@
|
|||
import os
|
||||
|
||||
from fastapi.responses import FileResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi import FastAPI
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def add_static_file(app: FastAPI, fname: str, folder: str):
|
||||
print(f"Adding route for /{fname}")
|
||||
def add_static_file(app: FastAPI, fname: str, folder: str) -> None:
|
||||
"""Add a single file to the root of the FastAPI app
|
||||
The file with name `fname` will be mounted at `/fname` - the
|
||||
`folder` does not affect where it is mounted in the app.
|
||||
|
||||
app: The FastAPI app to add to, in this case the OpenFlexure server
|
||||
fname: the name of the file to add
|
||||
folder: the containing folder of the file
|
||||
"""
|
||||
p = os.path.join(folder, fname)
|
||||
app.get(f"/{fname}", response_class=FileResponse, include_in_schema=False)(
|
||||
lambda: FileResponse(p)
|
||||
)
|
||||
|
||||
|
||||
def add_static_files(app: FastAPI):
|
||||
# with importlib.resources.as_file(openflexure_microscope_server) as p:
|
||||
# static_path = p.join("/static/")
|
||||
# TODO: don't hard code this!
|
||||
search_paths = [
|
||||
"/var/openflexure/application/openflexure-microscope-server/src/openflexure_microscope_server/static",
|
||||
pathlib.Path().absolute()
|
||||
/ "application/openflexure-microscope-server/src/openflexure_microscope_server/static",
|
||||
]
|
||||
if __file__:
|
||||
search_paths.append(pathlib.Path(__file__).parent.parent / "static")
|
||||
def add_static_files(app: FastAPI) -> None:
|
||||
"""Add the static files responsible for the webapp app to the FastAPI app
|
||||
|
||||
for static_path in search_paths:
|
||||
if os.path.isdir(static_path):
|
||||
break # stop once one of the paths exists
|
||||
else:
|
||||
# If we get to the else: block, no pat was found.
|
||||
raise RuntimeError("Can't find static files :(")
|
||||
app: The FastAPI app to add to, in this case the OpenFlexure server
|
||||
"""
|
||||
static_path = os.path.normpath(os.path.join(THIS_DIR, "..", "static"))
|
||||
|
||||
@app.get("/", response_class=RedirectResponse)
|
||||
async def redirect_fastapi():
|
||||
return "/index.html"
|
||||
|
||||
# Mounting the webapp at / file by file to allow other endpoints to be created
|
||||
for fname in os.listdir(static_path):
|
||||
fpath = os.path.join(static_path, fname)
|
||||
if os.path.isfile(fpath):
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ JPEGBlob = blob_type("image/jpeg")
|
|||
ZipBlob = blob_type("application/zip")
|
||||
IMG_DIR_NAME = "images"
|
||||
SCAN_DATA_FILENAME = "scan_data.json"
|
||||
STITCHING_CMD = "openflexure-stitch"
|
||||
|
||||
SCAN_ZERO_PAD_DIGITS = 4
|
||||
|
||||
|
|
@ -112,8 +113,7 @@ def _scan_running(method):
|
|||
|
||||
|
||||
class SmartScanThing(Thing):
|
||||
def __init__(self, path_to_openflexure_stitch: str):
|
||||
self._stitching_script = path_to_openflexure_stitch
|
||||
def __init__(self):
|
||||
self._preview_stitch_popen = None
|
||||
self._preview_stitch_popen_lock = threading.Lock()
|
||||
self._scan_lock = threading.Lock()
|
||||
|
|
@ -588,6 +588,9 @@ class SmartScanThing(Thing):
|
|||
self._return_to_starting_position()
|
||||
self._perform_final_stitch()
|
||||
|
||||
# Remove any scan folders containing zero images
|
||||
self.purge_empty_scans(logger=self._scan_logger)
|
||||
|
||||
@_scan_running
|
||||
def _main_scan_loop(self):
|
||||
"""
|
||||
|
|
@ -868,22 +871,26 @@ class SmartScanThing(Thing):
|
|||
404: {"description": "Scan not found"},
|
||||
},
|
||||
)
|
||||
def delete_scan(self, scan_name: str) -> None:
|
||||
"""Delete all files from a scan.
|
||||
def delete_scan(self, scan_name: str, logger: InvocationLogger) -> None:
|
||||
"""Delete the folder for the specified scan.
|
||||
|
||||
This endpoint allows scans to be deleted from disk.
|
||||
|
||||
Takes the scan name to delete, and the Invocation Logger
|
||||
"""
|
||||
path = os.path.join(self.base_scan_dir, scan_name)
|
||||
if not os.path.isdir(path):
|
||||
print(f"can't find {path}")
|
||||
raise HTTPException(404, "Scan not found")
|
||||
shutil.rmtree(path)
|
||||
logger.info(f"can't find {path}")
|
||||
raise HTTPException(400, "Scan not found")
|
||||
deleted_scan_success = self._delete_scan(path, logger)
|
||||
if not deleted_scan_success:
|
||||
raise HTTPException(400, "Couldn't delete scan, check log for details")
|
||||
|
||||
@fastapi_endpoint(
|
||||
"delete",
|
||||
"scans",
|
||||
)
|
||||
def delete_all_scans(self) -> None:
|
||||
def delete_all_scans(self, logger: InvocationLogger) -> None:
|
||||
"""Delete all the scans on the microscope
|
||||
|
||||
**This will irreversibly remove all scanned data from the
|
||||
|
|
@ -891,7 +898,20 @@ class SmartScanThing(Thing):
|
|||
Use with extreme caution.
|
||||
"""
|
||||
for scan in self.scans:
|
||||
self.delete_scan(scan.name)
|
||||
path = os.path.join(self.base_scan_dir, scan.name)
|
||||
self._delete_scan(path, logger)
|
||||
|
||||
@thing_action
|
||||
def _delete_scan(self, scan_path, logger: InvocationLogger) -> bool:
|
||||
try:
|
||||
shutil.rmtree(scan_path)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Attempted to delete scan " + scan_path + ", which failed."
|
||||
" Server sent response" + str(e)
|
||||
)
|
||||
return False
|
||||
|
||||
@property
|
||||
def latest_preview_stitch_path(self):
|
||||
|
|
@ -958,7 +978,7 @@ class SmartScanThing(Thing):
|
|||
with self._preview_stitch_popen_lock:
|
||||
self._preview_stitch_popen = Popen(
|
||||
[
|
||||
self._stitching_script,
|
||||
STITCHING_CMD,
|
||||
"--stitching_mode",
|
||||
"only_stage_stitch",
|
||||
"--minimum_overlap",
|
||||
|
|
@ -1072,7 +1092,7 @@ class SmartScanThing(Thing):
|
|||
self.run_subprocess(
|
||||
logger,
|
||||
[
|
||||
self._stitching_script,
|
||||
STITCHING_CMD,
|
||||
"--stitching_mode",
|
||||
"all",
|
||||
f"{tiff_arg}",
|
||||
|
|
@ -1179,3 +1199,26 @@ class SmartScanThing(Thing):
|
|||
"""List the relative paths of all files and folders in the zip folder specified"""
|
||||
scan_zip = zipfile.ZipFile(zip_path)
|
||||
return [os.path.normpath(i) for i in scan_zip.namelist()]
|
||||
|
||||
@thing_action
|
||||
def purge_empty_scans(self, logger: InvocationLogger) -> None:
|
||||
"""
|
||||
Delete all scan folders containing no images at the top level
|
||||
"""
|
||||
scan_list = self.scans
|
||||
|
||||
# Filter out scans with no top level files, ignoring JSON files
|
||||
# JSON is ignored as it's created before any images are captured
|
||||
for scan in scan_list:
|
||||
scan_folder = os.path.join(self.base_scan_dir, scan.name, IMG_DIR_NAME)
|
||||
images_found = False
|
||||
for fname in os.listdir(scan_folder):
|
||||
fpath = os.path.join(scan_folder, fname)
|
||||
if os.path.isfile(fpath) and IMAGE_REGEX.search(fname):
|
||||
images_found = True
|
||||
# break as soon as an image is found.
|
||||
break
|
||||
|
||||
if not images_found:
|
||||
path = os.path.join(self.base_scan_dir, scan.name)
|
||||
self._delete_scan(path, logger)
|
||||
|
|
|
|||
|
|
@ -80,7 +80,11 @@
|
|||
// Nav
|
||||
//
|
||||
|
||||
@nav-header-font-size: 12px;
|
||||
@nav-header-font-size: 16px;
|
||||
.uk-nav-header{
|
||||
text-transform:none;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Subnav
|
||||
|
|
@ -343,6 +347,11 @@ a:hover {
|
|||
a:hover {
|
||||
color: @inverse-primary-muted-color;
|
||||
}
|
||||
|
||||
.uk-alert-success{
|
||||
color: rgba(28, 131, 45);
|
||||
background-color: rgba(130, 221, 145, 0.671);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -353,6 +362,7 @@ a:hover {
|
|||
padding: 0 8px;
|
||||
border-color: @global-border;
|
||||
margin-bottom: 2px;
|
||||
text-transform:none !important;
|
||||
}
|
||||
|
||||
.uk-button-default {
|
||||
|
|
|
|||
|
|
@ -205,27 +205,27 @@ export default {
|
|||
topTabs: function() {
|
||||
let tabs = [
|
||||
{
|
||||
id: "view",
|
||||
id: "View",
|
||||
icon: "visibility",
|
||||
component: viewContent
|
||||
},
|
||||
{
|
||||
id: "navigate",
|
||||
id: "Navigate",
|
||||
icon: "gamepad",
|
||||
component: navigateContent
|
||||
},
|
||||
{
|
||||
id: "background detect",
|
||||
id: "Background Detect",
|
||||
icon: "background_replace",
|
||||
component: backgroundDetectContent
|
||||
},
|
||||
{
|
||||
id: "slide scan",
|
||||
id: "Slide Scan",
|
||||
icon: "settings_overscan",
|
||||
component: slideScanContent
|
||||
},
|
||||
{
|
||||
id: "scan list",
|
||||
id: "Scan List",
|
||||
icon: "photo_library",
|
||||
component: ScanListContent
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ export default {
|
|||
this.modalNotify(`The action '${this.submitLabel}' was cancelled.`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.$emit("error", error | Error("Unknown error"));
|
||||
this.$emit("error", error);
|
||||
} finally {
|
||||
// Reset taskRunning and taskId
|
||||
this.taskRunning = false;
|
||||
|
|
@ -297,7 +297,19 @@ export default {
|
|||
else if (result == "error") {
|
||||
// Pass the error string back with reject
|
||||
if (!this.progress) this.progress = 1;
|
||||
reject(new Error(response.data.output));
|
||||
// Test whether the log is empty or the most recent message is not from an error
|
||||
// If so, return a default message
|
||||
if (response.data.log.length == 0 | response.data.log.at(-1).levelname != "ERROR") {
|
||||
var message = "Unexpected error, please check the logs";
|
||||
}
|
||||
// As LabThings Actions add the message from any raised exception to the log, the
|
||||
// last message in the log is the message from the Exception.
|
||||
//If the Exception was raised with no message, use a default.
|
||||
else {
|
||||
message = response.data.log.at(-1).message||"Unexpected error, please check the logs";
|
||||
}
|
||||
// Raise an Error with the chosen message
|
||||
reject(new Error(message));
|
||||
}
|
||||
// If task ends without reporting an error
|
||||
// (NB this includes cancellation)
|
||||
|
|
|
|||
|
|
@ -25,12 +25,6 @@
|
|||
Apply
|
||||
</button>
|
||||
</form>
|
||||
<form class="uk-form-stacked" @submit.prevent="resetTour">
|
||||
<label class="uk-form-label">Re-run tour on next load</label>
|
||||
<button class="uk-button uk-button-default uk-margin-small">
|
||||
Reset
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
<div v-if="$store.state.available">
|
||||
<div>
|
||||
<div class="uk-margin-small-bottom">
|
||||
<b>Microscope hostname:</b>
|
||||
<b>Microscope Hostname:</b>
|
||||
<br />
|
||||
{{ $store.state.microscopeHostname }}
|
||||
</div>
|
||||
<div class="uk-margin-small-bottom">
|
||||
<b>API origin:</b>
|
||||
<b>API Origin:</b>
|
||||
<br />
|
||||
{{ $store.state.origin }}
|
||||
</div>
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
<hr />
|
||||
|
||||
<div>
|
||||
<b>Server version:</b> <br />
|
||||
<b>Server Version:</b> <br />
|
||||
TODO
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -19,14 +19,14 @@
|
|||
<propertyControl
|
||||
thing-name="background_detect"
|
||||
property-name="fraction"
|
||||
label="Sample coverage required (%)"
|
||||
label="Sample Coverage Required (%)"
|
||||
/>
|
||||
</div>
|
||||
<div class="uk-margin">
|
||||
<action-button
|
||||
thing="background_detect"
|
||||
action="background_fraction"
|
||||
submit-label="Check coverage"
|
||||
submit-label="Check Coverage"
|
||||
:can-terminate="false"
|
||||
:poll-interval="0.1"
|
||||
@response="alertBackgroundFraction"
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
<action-button
|
||||
thing="background_detect"
|
||||
action="set_background"
|
||||
submit-label="Set background"
|
||||
submit-label="Set Background"
|
||||
:can-terminate="false"
|
||||
:poll-interval="0.1"
|
||||
@response="alertBackgroundSet"
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
<action-button
|
||||
thing="background_detect"
|
||||
action="image_is_sample"
|
||||
submit-label="Check current image"
|
||||
submit-label="Check Current Image"
|
||||
:can-terminate="false"
|
||||
:poll-interval="0.1"
|
||||
@response="alertImageLabel"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<li>
|
||||
<a class="uk-accordion-title" href="#">Configure</a>
|
||||
<div class="uk-accordion-content">
|
||||
<b>STEP SIZE</b>
|
||||
<b>Step Size</b>
|
||||
<div class="uk-grid-small uk-child-width-1-3" uk-grid>
|
||||
<div>
|
||||
<label class="uk-form-label" for="form-stacked-text">x</label>
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
uk-grid
|
||||
>
|
||||
<div class="uk-width-xlarge">
|
||||
<h3>Camera/stage mapping</h3>
|
||||
<h3>Camera to Stage Mapping</h3>
|
||||
<p>
|
||||
Camera/stage mapping allows the stage to move relative to the camera
|
||||
Camera-stage mapping allows the stage to move relative to the camera
|
||||
view. This enables functions like click-to-move, and more precise tile
|
||||
scans.
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
"
|
||||
thing="camera_stage_mapping"
|
||||
action="calibrate_xy"
|
||||
:submit-label="'Auto-Calibrate using camera'"
|
||||
:submit-label="'Auto-Calibrate Using Camera'"
|
||||
:modal-progress="true"
|
||||
@response="onRecalibrateResponse"
|
||||
@error="modalError"
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
class="uk-button uk-button-default uk-width-1-1"
|
||||
@click="getCalibrationData()"
|
||||
>
|
||||
Download calibration data
|
||||
Download Calibration Data
|
||||
</button>
|
||||
<div v-if="this.csmMatrix!='undefined'" style="margin:10px;">
|
||||
<details><summary>Calibration Details</summary>
|
||||
|
|
|
|||
|
|
@ -13,19 +13,19 @@
|
|||
<a class="uk-accordion-title" href="#">Pi Camera Settings</a>
|
||||
<div class="uk-accordion-content">
|
||||
<PropertyControl
|
||||
label="Exposure time (0-33251)"
|
||||
label="Exposure Time (0-33251)"
|
||||
property-name="exposure_time"
|
||||
thing-name="camera"
|
||||
:read-back-delay="1000"
|
||||
/>
|
||||
<PropertyControl
|
||||
label="Analogue gain"
|
||||
label="Analogue Gain"
|
||||
property-name="analogue_gain"
|
||||
thing-name="camera"
|
||||
:read-back-delay="1000"
|
||||
/>
|
||||
<PropertyControl
|
||||
label="Colour gains"
|
||||
label="Colour Gains"
|
||||
property-name="colour_gains"
|
||||
thing-name="camera"
|
||||
:read-back-delay="1000"
|
||||
|
|
@ -36,13 +36,13 @@
|
|||
<a class="uk-accordion-title" href="#">Image Quality</a>
|
||||
<div class="uk-accordion-content">
|
||||
<PropertyControl
|
||||
label="MJPEG stream bit rate"
|
||||
label="MJPEG Stream Bit Rate"
|
||||
property-name="mjpeg_bitrate"
|
||||
thing-name="camera"
|
||||
:read-back-delay="100"
|
||||
/>
|
||||
<PropertyControl
|
||||
label="MJPEG stream resolution"
|
||||
label="MJPEG Stream Resolution"
|
||||
property-name="stream_resolution"
|
||||
thing-name="camera"
|
||||
:read-back-delay="100"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
:requires-confirmation="false"
|
||||
thing="camera"
|
||||
action="auto_expose_from_minimum"
|
||||
:submit-label="'Auto gain & shutter speed'"
|
||||
:submit-label="'Auto Gain & Shutter Speed'"
|
||||
@response="onRecalibrateResponse"
|
||||
@error="modalError"
|
||||
/>
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
:requires-confirmation="false"
|
||||
thing="camera"
|
||||
action="calibrate_white_balance"
|
||||
:submit-label="'Auto white balance'"
|
||||
:submit-label="'Auto White Balance'"
|
||||
@response="onRecalibrateResponse"
|
||||
@error="modalError"
|
||||
/>
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
"
|
||||
thing="camera"
|
||||
action="calibrate_lens_shading"
|
||||
:submit-label="'Auto flat field correction'"
|
||||
:submit-label="'Auto Flat Field Correction'"
|
||||
@response="onRecalibrateResponse"
|
||||
@error="modalError"
|
||||
/>
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
:requires-confirmation="false"
|
||||
thing="camera"
|
||||
action="flat_lens_shading"
|
||||
:submit-label="'Disable flat field correction'"
|
||||
:submit-label="'Disable Flat Field Correction'"
|
||||
@response="onRecalibrateResponse"
|
||||
@error="modalError"
|
||||
/>
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
:requires-confirmation="false"
|
||||
thing="camera"
|
||||
action="reset_lens_shading"
|
||||
:submit-label="'Reset flat field correction'"
|
||||
:submit-label="'Reset Flat Field Correction'"
|
||||
@response="onRecalibrateResponse"
|
||||
@error="modalError"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
<template>
|
||||
<div id="appSettings" class="uk-width-large">
|
||||
<h3>Additional features</h3>
|
||||
<p class="uk-margin-small" :class="{ 'uk-text-muted': !imjoyPermitted }">
|
||||
<label :disabled="!imjoyPermitted">
|
||||
<input
|
||||
v-model="imjoyEnabled"
|
||||
class="uk-checkbox"
|
||||
type="checkbox"
|
||||
:disabled="!imjoyPermitted"
|
||||
/>
|
||||
Enable ImJoy plugin engine
|
||||
</label>
|
||||
</p>
|
||||
<p v-if="imjoyPermitted" class="uk-margin-small">
|
||||
<a href="https://imjoy.io/">ImJoy</a> enables integration with a wide
|
||||
variety of microscopy and image analysis applications, including ImageJ.JS
|
||||
and Kaibu. Support for ImJoy within the OFM software is currently
|
||||
experimental, and it may slow down loading of the application.
|
||||
</p>
|
||||
<p v-if="!imjoyPermitted" class="uk-margin-small uk-text-muted">
|
||||
ImJoy plugins are disabled in this build of the OpenFlexure software.
|
||||
</p>
|
||||
<p class="uk-margin-small">
|
||||
<label
|
||||
><input v-model="galleryEnabled" class="uk-checkbox" type="checkbox" />
|
||||
Enable Gallery</label
|
||||
>
|
||||
</p>
|
||||
<p class="uk-margin-small">
|
||||
The "gallery" tab can cause performance issues; un-tick the box above to
|
||||
disable it.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Export main app
|
||||
export default {
|
||||
name: "FeaturesSettings",
|
||||
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
|
||||
computed: {
|
||||
imjoyEnabled: {
|
||||
get() {
|
||||
return this.$store.state.imjoyEnabled;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("changeImjoyEnabled", value);
|
||||
}
|
||||
},
|
||||
galleryEnabled: {
|
||||
get() {
|
||||
return this.$store.state.galleryEnabled;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("changeGalleryEnabled", value);
|
||||
}
|
||||
},
|
||||
imjoyPermitted: function() {
|
||||
// ImJoy may be disabled using an environment variable.
|
||||
// If this function returns false, it is never possible to
|
||||
// use ImJoy, so we should gray out the option.
|
||||
return process.env.VUE_APP_ENABLE_IMJOY === "true";
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
imjoyEnabled: function() {
|
||||
this.setLocalStorageObj("imjoyEnabled", this.imjoyEnabled);
|
||||
},
|
||||
galleryEnabled: function() {
|
||||
this.setLocalStorageObj("galleryEnabled", this.galleryEnabled);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Try loading settings from localStorage. If null, don't change.
|
||||
this.imjoyEnabled =
|
||||
this.getLocalStorageObj("imjoyEnabled") || this.imjoyEnabled;
|
||||
this.galleryEnabled =
|
||||
this.getLocalStorageObj("galleryEnabled") || this.galleryEnabled;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<div id="streamSettings" class="uk-width-large">
|
||||
<div>
|
||||
<h3>Stream settings</h3>
|
||||
<h3>Stream Settings</h3>
|
||||
<label
|
||||
><input v-model="disableStream" class="uk-checkbox" type="checkbox" />
|
||||
Disable web stream</label
|
||||
Disable Web Stream</label
|
||||
>
|
||||
<p class="uk-margin-small">
|
||||
This will disable the embedded web stream of the camera.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove">
|
||||
<div class="settings-nav">
|
||||
<ul class="uk-nav uk-nav-default">
|
||||
<li class="uk-nav-header">Application settings</li>
|
||||
<li class="uk-nav-header">Application Settings</li>
|
||||
<li>
|
||||
<tabIcon
|
||||
id="settings-display-icon"
|
||||
|
|
@ -17,20 +17,7 @@
|
|||
Display
|
||||
</tabIcon>
|
||||
</li>
|
||||
<li>
|
||||
<tabIcon
|
||||
id="settings-features-icon"
|
||||
tab-i-d="features"
|
||||
:show-title="false"
|
||||
:show-tooltip="false"
|
||||
:require-connection="false"
|
||||
:current-tab="currentTab"
|
||||
@set-tab="setTab"
|
||||
>
|
||||
Features
|
||||
</tabIcon>
|
||||
</li>
|
||||
<li class="uk-nav-header">Microscope settings</li>
|
||||
<li class="uk-nav-header">Microscope Settings</li>
|
||||
<li>
|
||||
<tabIcon
|
||||
id="settings-camera-icon"
|
||||
|
|
@ -67,15 +54,14 @@
|
|||
:current-tab="currentTab"
|
||||
@set-tab="setTab"
|
||||
>
|
||||
Camera/stage mapping
|
||||
Camera to Stage Mapping
|
||||
</tabIcon>
|
||||
</li>
|
||||
</ul>
|
||||
<action-button
|
||||
thing="settings"
|
||||
action="save_all_thing_settings"
|
||||
submit-label="Save all settings"
|
||||
class="uk-margin"
|
||||
submit-label="Save All Settings"
|
||||
/>
|
||||
</div>
|
||||
<div class="view-component uk-width-expand uk-padding-small">
|
||||
|
|
@ -90,16 +76,6 @@
|
|||
</div>
|
||||
</tabContent>
|
||||
|
||||
<tabContent
|
||||
tab-i-d="features"
|
||||
:require-connection="false"
|
||||
:current-tab="currentTab"
|
||||
>
|
||||
<div class="settings-pane uk-padding-small">
|
||||
<featuresSettings />
|
||||
</div>
|
||||
</tabContent>
|
||||
|
||||
<tabContent
|
||||
tab-i-d="camera"
|
||||
:require-connection="true"
|
||||
|
|
@ -137,7 +113,6 @@
|
|||
import streamSettings from "./settingsComponents/streamSettings.vue";
|
||||
import cameraSettings from "./settingsComponents/cameraSettings.vue";
|
||||
import appSettings from "./settingsComponents/appSettings.vue";
|
||||
import featuresSettings from "./settingsComponents/featuresSettings.vue";
|
||||
import CSMSettings from "./settingsComponents/CSMSettings.vue";
|
||||
import stageSettings from "./settingsComponents/stageSettings.vue";
|
||||
// Import generic components
|
||||
|
|
@ -155,7 +130,6 @@ export default {
|
|||
stageSettings,
|
||||
CSMSettings,
|
||||
appSettings,
|
||||
featuresSettings,
|
||||
tabIcon,
|
||||
tabContent,
|
||||
ActionButton
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<propertyControl
|
||||
thing-name="smart_scan"
|
||||
property-name="autofocus_dz"
|
||||
label="Autofocus range (steps)"
|
||||
label="Autofocus Range (steps)"
|
||||
/>
|
||||
</div>
|
||||
<div class="uk-margin">
|
||||
|
|
@ -35,21 +35,21 @@
|
|||
<propertyControl
|
||||
thing-name="autofocus"
|
||||
property-name="stack_images_to_capture"
|
||||
label="Images in stack to capture"
|
||||
label="Images in Stack to Capture"
|
||||
/>
|
||||
</div>
|
||||
<div class="uk-margin">
|
||||
<propertyControl
|
||||
thing-name="smart_scan"
|
||||
property-name="overlap"
|
||||
label="Image overlap (0-1)"
|
||||
label="Image Overlap (0-1)"
|
||||
/>
|
||||
</div>
|
||||
<div class="uk-margin">
|
||||
<propertyControl
|
||||
thing-name="smart_scan"
|
||||
property-name="stitch_tiff"
|
||||
label="When stitching, produce a pyramidal tiff"
|
||||
label="When Stitching, Produce a Pyramidal TIFF"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -61,14 +61,14 @@
|
|||
<propertyControl
|
||||
thing-name="smart_scan"
|
||||
property-name="skip_background"
|
||||
label="Detect and skip empty fields"
|
||||
label="Detect and Skip Empty Fields"
|
||||
/>
|
||||
</div>
|
||||
<div class="uk-margin">
|
||||
<propertyControl
|
||||
thing-name="smart_scan"
|
||||
property-name="stitch_automatically"
|
||||
label="Automatically stitch images together"
|
||||
label="Automatically Stitch Images Together"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -89,7 +89,7 @@
|
|||
thing="smart_scan"
|
||||
action="sample_scan"
|
||||
:submit-data="{ scan_name: scan_name }"
|
||||
submit-label="Start smart scan"
|
||||
submit-label="Start Smart Scan"
|
||||
:can-terminate="true"
|
||||
@taskStarted="startScanning"
|
||||
@update:taskStatus="taskStatus = $event"
|
||||
|
|
|
|||
|
|
@ -144,10 +144,16 @@ Vue.mixin({
|
|||
if (error.response) {
|
||||
// If the response is a nicely formatted JSON response from the server
|
||||
if (error.response.data.message) {
|
||||
return `${error.response.status}: ${error.response.data.message}`;
|
||||
return `${error.response.data.message}`;
|
||||
}
|
||||
if (error.response.data.detail) {
|
||||
return `${error.response.data.detail}`;
|
||||
}
|
||||
// If the response is just some generic error response
|
||||
return `${error.response.status}: ${error.response.data}`;
|
||||
if (error.response.data){
|
||||
return `${error.response.data}`;
|
||||
}
|
||||
return `${error.response}`;
|
||||
}
|
||||
// If we have an error object with a message, use that
|
||||
if (error.message) return `${error.message}`;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue