Merge branch 'master' into imjoy-support

This commit is contained in:
Richard 2021-05-29 12:10:01 +01:00
commit dac1c9a748
21 changed files with 994 additions and 2122 deletions

View file

@ -3935,38 +3935,12 @@
"dev": true
},
"axios": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
"dev": true,
"requires": {
"follow-redirects": "1.5.10"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"dev": true,
"requires": {
"debug": "=3.1.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
"follow-redirects": "^1.10.0"
}
},
"babel-eslint": {

View file

@ -23,7 +23,7 @@
"@vue/cli-plugin-eslint": "^4.5.9",
"@vue/cli-service": "^4.5.9",
"@vue/eslint-config-prettier": "^6.0.0",
"axios": "^0.19.2",
"axios": "^0.21.1",
"babel-eslint": "^10.1.0",
"css-loader": "^3.6.0",
"eslint": "^6.8.0",

View file

@ -194,6 +194,8 @@ import loggingContent from "./tabContentComponents/loggingContent.vue";
import calibrationModal from "./modalComponents/calibrationModal.vue";
import TabIcon from "./genericComponents/tabIcon.vue";
import { mapState } from "vuex";
// Export main app
export default {
name: "AppContent",
@ -302,21 +304,22 @@ export default {
return process.env.VUE_APP_ENABLE_IMJOY === "true";
},
...mapState("imjoy", { imjoyTabs: "tabs" })
// Map the tabs from ImJoy's store module so we can display them
...mapState("imjoy", { imjoyTabs: "tabs" }),
// Map the setting for IHI's interface so we can watch it
...mapState(["IHIEnabled"])
},
watch: {
// Update the interface when the IHI interface is enabled/disabled
IHIEnabled: function(newValue) {
this.updateTopTabs(newValue);
}
},
created: function() {
if (this.$store.getters.ready) {
// Detect local connection
if (
["localhost", "0.0.0.0", "127.0.0.1", "[::1]"].includes(
window.location.hostname
)
) {
this.$store.commit("changeDisableStream", true);
this.$store.commit("changeAutoGpuPreview", true);
this.$store.commit("changeTrackWindow", true);
}
// Update top tabs
this.updateTopTabs(this.$store.state.IHIEnabled);
// Update plugins
@ -325,16 +328,6 @@ export default {
this.startModals();
});
}
// Watch for host 'ready', then update status
this.unwatchStoreFunction = this.$store.watch(
state => {
return state.IHIEnabled;
},
IHIEnabled => {
this.updateTopTabs(IHIEnabled);
}
);
},
mounted() {
@ -352,14 +345,6 @@ export default {
});
},
beforeDestroy() {
// Then we call that function here to unwatch
if (this.unwatchStoreFunction) {
this.unwatchStoreFunction();
this.unwatchStoreFunction = null;
}
},
methods: {
updateTopTabs: function(IHIEnabled) {
if (IHIEnabled) {

View file

@ -1,12 +1,36 @@
<template>
<div>
<form class="uk-form-stacked" @submit.prevent="overrideAPIHost">
<form
class="uk-form-stacked"
action=""
method="GET"
@submit="overrideAPIHost"
>
<label class="uk-form-label">Override API origin</label>
<input v-model="newOrigin" class="uk-input" type="text" />
<input
v-model="newOrigin"
name="overrideOrigin"
class="uk-input"
type="text"
/>
<label class="uk-form-label">
<input
v-model="reloadWhenOverridingOrigin"
class="uk-input uk-checkbox"
type="checkbox"
/>
Reload web app with new origin
</label>
<button class="uk-button uk-button-default uk-margin-small">
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>
@ -19,22 +43,35 @@ export default {
data: function() {
return {
newOrigin: this.$store.state.origin
newOrigin: this.$store.state.origin,
reloadWhenOverridingOrigin: true
};
},
mounted() {
if (localStorage.overrideOrigin){
if (localStorage.overrideOrigin) {
this.newOrigin = localStorage.overrideOrigin;
}else{
} else {
this.newOrigin = "http://microscope.local:5000";
}
},
methods: {
overrideAPIHost: function() {
this.$store.commit("changeOrigin", this.newOrigin);
localStorage.overrideOrigin = this.newOrigin
overrideAPIHost: function(event) {
// Save the origin override, so that if we reload the web app, you can easily
localStorage.overrideOrigin = this.newOrigin;
// If we have elected not to reload the interface, just update the origin
// in the store. Otherwise, the form's default action will do the job for us.
// TODO: preserve other query parameters when reloading
if (!this.reloadWhenOverridingOrigin) {
this.$store.commit("changeOrigin", this.newOrigin);
event.preventDefault();
}
},
resetTour: function() {
// Make the introduction tour run next time the app loads
this.setLocalStorageObj("completedTour", false);
}
}
};

View file

@ -155,7 +155,7 @@
v-model="scanStepSize.z"
class="uk-input uk-form-small"
type="number"
name="inputPositionZx"
name="inputPositionZ"
/>
</div>
</div>
@ -172,6 +172,7 @@
class="uk-input uk-form-small"
type="number"
name="inputPositionX"
min="1"
/>
</div>
</div>
@ -186,6 +187,7 @@
class="uk-input uk-form-small"
type="number"
name="inputPositionY"
min="1"
/>
</div>
</div>
@ -199,7 +201,8 @@
v-model="scanSteps.z"
class="uk-input uk-form-small"
type="number"
name="inputPositionZx"
name="inputPositionZ"
min="1"
/>
</div>
</div>

View file

@ -3,7 +3,7 @@
<div class="uk-grid uk-grid-divider uk-child-width-expand" uk-grid>
<div class="uk-width-large">
<h3>Manual camera settings</h3>
<form @submit.prevent="applySettingsRequest">
<form @submit.prevent="applySettingsRequest" >
<div class="uk-margin-small-bottom">
<ul uk-accordion="multiple: true">
<li class="uk-open">
@ -31,7 +31,7 @@
v-model="picamera.analog_gain"
class="uk-input uk-form-small"
type="number"
step="0.000001"
step="0.0000000000000001"
/>
</div>
</div>
@ -45,7 +45,29 @@
v-model="picamera.digital_gain"
class="uk-input uk-form-small"
type="number"
step="0.000001"
step="0.0000000000000001"
/>
</div>
</div>
<div v-if="picamera.awb_gains !== undefined">
<label class="uk-form-label" for="form-stacked-text" >
White Balance gains
</label>
<div class="uk-form-controls">
<label class="uk-form-label" >R:</label>
<input
v-model="picamera.awb_gains[0]"
class="uk-input uk-form-small"
type="number"
step="0.0000000000000001"
/>
<label class="uk-form-label" >B:</label>
<input
v-model="picamera.awb_gains[1]"
class="uk-input uk-form-small"
type="number"
step="0.0000000000000001"
/>
</div>
</div>
@ -194,7 +216,8 @@ export default {
shutter_speed: undefined,
analog_gain: undefined,
digital_gain: undefined,
framerate: undefined
framerate: undefined,
awb_gains: undefined
},
mjpeg_bitrate: undefined,
stream_resolution: undefined,
@ -244,6 +267,7 @@ export default {
this.picamera.digital_gain = cameraSettings.picamera.digital_gain;
this.picamera.shutter_speed = cameraSettings.picamera.shutter_speed;
this.picamera.framerate = cameraSettings.picamera.framerate;
this.picamera.awb_gains = cameraSettings.picamera.awb_gains;
}
})
.catch(error => {
@ -263,7 +287,11 @@ export default {
shutter_speed: parseFloat(this.picamera.shutter_speed),
analog_gain: parseFloat(this.picamera.analog_gain),
digital_gain: parseFloat(this.picamera.digital_gain),
framerate: parseInt(this.picamera.framerate)
framerate: parseInt(this.picamera.framerate),
awb_gains: [
parseFloat(this.picamera.awb_gains[0]),
parseFloat(this.picamera.awb_gains[1]),
],
}
}
};

View file

@ -9,7 +9,44 @@
'Start recalibration? This may take a while, and the microscope will be locked during this time.'
"
:submit-url="recalibrationLinks.recalibrate.href"
:submit-label="'Auto-Calibrate'"
:submit-label="'Full Auto-Calibrate'"
@response="onRecalibrateResponse"
@error="modalError"
>
</taskSubmitter>
</div>
<div v-if="'auto_exposure_from_raw' in recalibrationLinks" class="uk-margin-small">
<taskSubmitter
:can-terminate="false"
:requires-confirmation="false"
:submit-url="recalibrationLinks.auto_exposure_from_raw.href"
:submit-label="'Auto gain &amp; shutter speed'"
@response="onRecalibrateResponse"
@error="modalError"
>
</taskSubmitter>
</div>
<div v-if="'auto_white_balance_from_raw' in recalibrationLinks" class="uk-margin-small">
<taskSubmitter
:can-terminate="false"
:requires-confirmation="false"
:submit-url="recalibrationLinks.auto_white_balance_from_raw.href"
:submit-label="'Auto white balance'"
@response="onRecalibrateResponse"
@error="modalError"
>
</taskSubmitter>
</div>
<div v-if="'auto_lens_shading_table' in recalibrationLinks" class="uk-margin-small">
<taskSubmitter
:can-terminate="false"
:requires-confirmation="true"
:confirmation-message="
'Is the microscope looking at an evenly illuminated, empty field of view? ' +
'If not, the current image will show through in any images captured afterwards.'
"
:submit-url="recalibrationLinks.auto_lens_shading_table.href"
:submit-label="'Auto flat field correction'"
@response="onRecalibrateResponse"
@error="modalError"
>
@ -22,15 +59,7 @@
class="uk-button uk-button-danger uk-width-1-1"
@click="flattenLensShadingTableRequest"
>
Disable flat-field correction
</button>
<button
v-if="'delete_lens_shading_table' in recalibrationLinks"
class="uk-button uk-button-danger uk-margin-small-top uk-width-1-1"
@click="deleteLensShadingTableRequest"
>
Adaptive flat-field correction
Disable flat field correction
</button>
</div>

View file

@ -33,7 +33,6 @@ export default {
},
set(value) {
this.$store.commit("changeIHIEnabled", value);
this.$root.$emit("globalSafeTogglePreview", value);
}
}
},

View file

@ -73,8 +73,11 @@ export default {
return this.$store.state.autoGpuPreview;
},
set(value) {
// NB the stream viewer watches the store, and is
// responsible for making the request that switches
// GPU preview on/off
// see streamContent.vue
this.$store.commit("changeAutoGpuPreview", value);
this.$root.$emit("globalSafeTogglePreview", value);
}
},
@ -86,6 +89,51 @@ export default {
this.$store.commit("changeTrackWindow", value);
}
}
},
watch: {
// Cache the stream settings to local storage for persistence
// (the next 3 functions all relate to this)
disableStream: function(newValue) {
console.log(
`disableStream updated to ${newValue} and saved in local storage`
);
this.setLocalStorageObj("disableStream", newValue);
},
autoGpuPreview: function(newValue) {
console.log(
`GPU preview updated to ${newValue} and saved in local storage`
);
this.setLocalStorageObj("autoGpuPreview", newValue);
},
trackWindow: function(newValue) {
console.log(
`trackWindow updated to ${newValue} and saved in local storage`
);
this.setLocalStorageObj("trackWindow", newValue);
}
},
created() {
// Apply sensible defaults for stream settings, depending on
// whether we're connecting locally or remotely, respecting
// the settings that were cached previously.
const localMode = ["localhost", "0.0.0.0", "127.0.0.1", "[::1]"].includes(
window.location.hostname
);
const localDefaults = {
disableStream: true,
autoGpuPreview: true,
trackWindow: true
};
for (let k in localDefaults) {
if (localStorage.getItem(k) !== null) {
this[k] = this.getLocalStorageObj(k);
} else if (localMode) {
console.log(`${k} set to default value for a local connection`);
this[k] = localDefaults[k];
}
}
}
};
</script>

View file

@ -69,6 +69,16 @@ export default {
},
settingsUri: function() {
return `${this.$store.getters.baseUri}/api/v2/instrument/settings`;
},
autoGpuPreview: function() {
return this.$store.state.autoGpuPreview;
}
},
watch: {
autoGpuPreview: function(newValue) {
// When the GPU preview setting in the store changes, update the server
this.safePreviewRequest(newValue);
}
},
@ -77,9 +87,6 @@ export default {
this.$root.$on("globalTogglePreview", state => {
this.previewRequest(state);
});
this.$root.$on("globalSafeTogglePreview", state => {
this.safePreviewRequest(state);
});
// A global signal listener to flash the stream element
this.$root.$on("globalFlashStream", () => {
this.flashStream();
@ -97,7 +104,7 @@ export default {
created: function() {
// Send a request to start/stop GPU preview based on global setting
this.safePreviewRequest(this.$store.state.autoGpuPreview);
this.safePreviewRequest(this.autoGpuPreview);
},
beforeDestroy: function() {

View file

@ -61,12 +61,25 @@ const moduleImjoy = {
getters: {}
};
function getOriginFromLocation() {
// This will default to the same origin that's serving
// the web app - but can be overridden by the URL.
// See also devTools.vue which can change the origin.
let url = new URL(window.location.href);
let origin = url.searchParams.get("overrideOrigin");
if (origin) {
return origin;
} else {
return url.origin;
}
}
export default new Vuex.Store({
modules: {
imjoy: moduleImjoy
},
state: {
origin: window.location.origin,
origin: getOriginFromLocation(),
available: false,
waiting: false,
error: "",