Added app setting to enable/disable IHI slidescan

This commit is contained in:
Joel Collins 2020-09-09 12:54:44 +01:00
parent de7f4f6dfe
commit f0ce85fc3f
6 changed files with 209 additions and 120 deletions

View file

@ -55,24 +55,20 @@
> >
<i class="material-icons">camera_alt</i> <i class="material-icons">camera_alt</i>
</tabIcon> </tabIcon>
<tabIcon
id="slidescan-tab-icon" <template v-if="$store.state.globalSettings.IHIEnabled">
tab-i-d="slidescan" <hr id="extension-tab-divider" />
:require-connection="true"
:current-tab="currentTab" <tabIcon
@set-tab="setTab" id="slidescan-tab-icon"
> tab-i-d="slidescan"
<i class="material-icons">camera_alt</i> :require-connection="true"
</tabIcon> :current-tab="currentTab"
<tabIcon @set-tab="setTab"
id="settings-tab-icon" >
tab-i-d="settings" <i class="material-icons">camera_alt</i>
:require-connection="false" </tabIcon>
:current-tab="currentTab" </template>
@set-tab="setTab"
>
<i class="material-icons">settings</i>
</tabIcon>
<hr id="extension-tab-divider" /> <hr id="extension-tab-divider" />
@ -90,8 +86,18 @@
</tabIcon> </tabIcon>
<tabIcon <tabIcon
id="about-tab-icon" id="settings-tab-icon"
class="uk-margin-auto-top" class="uk-margin-auto-top"
tab-i-d="settings"
:require-connection="false"
:current-tab="currentTab"
@set-tab="setTab"
>
<i class="material-icons">settings</i>
</tabIcon>
<tabIcon
id="about-tab-icon"
tab-i-d="about" tab-i-d="about"
:require-connection="false" :require-connection="false"
:current-tab="currentTab" :current-tab="currentTab"
@ -205,6 +211,7 @@ export default {
tabContent, tabContent,
navigateContent, navigateContent,
captureContent, captureContent,
slideScanContent,
viewContent, viewContent,
settingsContent, settingsContent,
galleryContent, galleryContent,

View file

@ -4,44 +4,61 @@
<div v-show="scanUri"> <div v-show="scanUri">
<h1>Sample Scan Wizard</h1> <h1>Sample Scan Wizard</h1>
<form v-on:submit.prevent v-on:keyup.enter="increment()"> <form @submit.prevent @keyup.enter="increment()">
<div id="step-user" v-show="stepValue==0"> <div v-show="stepValue == 0" id="step-user">
<h2>User information (1/4)</h2> <h2>User information (1/4)</h2>
<label for="username">Your name:</label> <label for="username">Your name:</label>
<input type="text" id="username" name="username" v-model="username" required /> <input
id="username"
v-model="username"
type="text"
name="username"
required
/>
<br /> <br />
<label for="currentTime">Current time (check this is correct):</label> <label for="currentTime">Current time (check this is correct):</label>
<input <input
type="datetime-local"
id="currentTime" id="currentTime"
name="currentTime"
v-model="currentTimeForm" v-model="currentTimeForm"
type="datetime-local"
name="currentTime"
/> />
</div> </div>
<div id="step-patient" v-show="stepValue==1"> <div v-show="stepValue == 1" id="step-patient">
<h2>Patient information (2/4)</h2> <h2>Patient information (2/4)</h2>
<label for="patientID">Patient ID:</label> <label for="patientID">Patient ID:</label>
<input type="text" id="patientID" name="patientID" v-model="patientID" required /> <input
id="patientID"
v-model="patientID"
type="text"
name="patientID"
required
/>
<br /> <br />
</div> </div>
<div id="step-sample" v-show="stepValue==2"> <div v-show="stepValue == 2" id="step-sample">
<h2>Sample information (3/4)</h2> <h2>Sample information (3/4)</h2>
<label>Sample type:</label> <label>Sample type:</label>
<br /> <br />
<input type="radio" id="typeThin" value="thin" v-model="sampleType" /> <input id="typeThin" v-model="sampleType" type="radio" value="thin" />
<label for="typeThin">Thin smear</label> <label for="typeThin">Thin smear</label>
<br /> <br />
<input type="radio" id="typeThick" value="thick" v-model="sampleType" /> <input
id="typeThick"
v-model="sampleType"
type="radio"
value="thick"
/>
<label for="typeThick">Thick smear</label> <label for="typeThick">Thick smear</label>
<br /> <br />
</div> </div>
<div id="step-scan" v-show="stepValue==3"> <div v-show="stepValue == 3" id="step-scan">
<h2>Start scan (4/4)</h2> <h2>Start scan (4/4)</h2>
<label>Check details:</label> <label>Check details:</label>
@ -62,38 +79,44 @@
<div id="form-stepper"> <div id="form-stepper">
<p class="warning">{{ message }}</p> <p class="warning">{{ message }}</p>
<taskSubmitter <taskSubmitter
v-if="scanUri" v-if="scanUri"
v-show="stepValue == 3" v-show="stepValue == 3"
:base-url="baseURL" :base-url="baseURL"
:submit-url="scanUri" :submit-url="scanUri"
:submit-data="payload" :submit-data="payload"
submit-label="Start scan" submit-label="Start scan"
@submit="scanRunning=true" @submit="scanRunning = true"
@response="scanRunning=false" @response="scanRunning = false"
@error="scanRunning=false" @error="scanRunning = false"
></taskSubmitter> ></taskSubmitter>
<br /> <br />
<div class="grid-container"> <div class="grid-container">
<button <button
:disabled="stepValue <= 0 || scanRunning" :disabled="stepValue <= 0 || scanRunning"
type="button" type="button"
v-on:click="decrement()" @click="decrement()"
>Previous</button> >
Previous
</button>
<button <button
:disabled="stepValue >= 3 || scanRunning" :disabled="stepValue >= 3 || scanRunning"
type="button" type="button"
v-on:click="increment()" @click="increment()"
>Next</button> >
Next
</button>
</div> </div>
<p> <p>
<button <button
v-show="stepValue == 3" v-show="stepValue == 3"
:disabled="scanRunning" :disabled="scanRunning"
type="button" type="button"
v-on:click="restart()" @click="restart()"
>Restart</button> >
Restart
</button>
</p> </p>
</div> </div>
</div> </div>
@ -124,14 +147,60 @@ export default {
}; };
}, },
computed: {
pluginsUri: function() {
return `${this.$store.getters.baseUri}/api/v2/extensions`;
},
currentTimeForm: {
get() {
// Chop the timezone information from the end
return this.currentTime.substr(0, 19);
},
set(val) {
console.log(val);
// Get timezone
var dt = new Date();
var tzo = -dt.getTimezoneOffset(),
dif = tzo >= 0 ? "+" : "-",
pad = function(num) {
var norm = Math.floor(Math.abs(num));
return (norm < 10 ? "0" : "") + norm;
};
// Stick to the end of the new timestring from the form
this.currentTime = val + dif + pad(tzo / 60) + ":" + pad(tzo % 60);
}
},
payload: {
get() {
return {
filename: `${this.patientID}_${this.sampleType}`,
temporary: false,
bayer: false,
grid: [10, 10, 9],
stride_size: [800, 600, 10],
fast_autofocus: true,
autofocus_dz: 2000,
use_video_port: false,
annotations: {
patientID: this.patientID,
username: this.username,
clientDatetime: this.currentTime
},
tags: ["medscan"]
};
}
}
},
watch: { watch: {
baseURL: function (val) { baseURL: function(val) {
if (this.baseURL) { if (this.baseURL) {
this.updateScanUri(); this.updateScanUri();
} else { } else {
this.message = "No baseURL given"; this.message = "No baseURL given";
} }
}, }
}, },
mounted: function() { mounted: function() {
@ -147,7 +216,6 @@ export default {
axios axios
.get(this.pluginsUri) // Get a list of plugins .get(this.pluginsUri) // Get a list of plugins
.then(response => { .then(response => {
console.log(response);
var plugins = response.data; var plugins = response.data;
var foundExtension = plugins.find( var foundExtension = plugins.find(
e => e.title === "org.openflexure.scan" e => e.title === "org.openflexure.scan"
@ -159,7 +227,7 @@ export default {
} }
}) })
.catch(error => { .catch(error => {
console.log(error); // Let mixin handle error this.modalError(error); // Let mixin handle error
}); });
}, },
@ -223,53 +291,6 @@ export default {
pad(tzo % 60) pad(tzo % 60)
); );
} }
},
computed: {
pluginsUri: function() {
return `${this.baseURL}/extensions`;
},
currentTimeForm: {
get() {
// Chop the timezone information from the end
return this.currentTime.substr(0, 19);
},
set(val) {
console.log(val);
// Get timezone
var dt = new Date();
var tzo = -dt.getTimezoneOffset(),
dif = tzo >= 0 ? "+" : "-",
pad = function(num) {
var norm = Math.floor(Math.abs(num));
return (norm < 10 ? "0" : "") + norm;
};
// Stick to the end of the new timestring from the form
this.currentTime = val + dif + pad(tzo / 60) + ":" + pad(tzo % 60);
}
},
payload: {
get() {
return {
filename: `${this.patientID}_${this.sampleType}`,
temporary: false,
bayer: false,
grid: [10, 10, 9],
stride_size: [800, 600, 10],
fast_autofocus: true,
autofocus_dz: 2000,
use_video_port: false,
annotations: {
patientID: this.patientID,
username: this.username,
clientDatetime: this.currentTime
},
tags: ["medscan"]
};
}
}
} }
}; };
</script> </script>
@ -281,23 +302,6 @@ export default {
text-align: left; text-align: left;
} }
input[type="text"],
input[type="datetime-local"] {
display: block;
margin: 0 10px 0 0;
width: 100%;
height: 25px;
}
button {
display: inline-block;
box-sizing: border-box;
margin: 0 10px 0 0;
width: 100%;
height: 25px;
min-width: 100px;
}
.grid-container { .grid-container {
display: grid; display: grid;
grid-template-columns: auto auto; grid-template-columns: auto auto;
@ -309,4 +313,4 @@ button {
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
} }
</style> </style>

View file

@ -15,7 +15,7 @@ import paneSlideScan from "../controlComponents/paneSlideScan";
import streamDisplay from "../viewComponents/streamDisplay.vue"; import streamDisplay from "../viewComponents/streamDisplay.vue";
export default { export default {
name: "NavigateContent", name: "SlideScanContent",
components: { components: {
paneSlideScan, paneSlideScan,

View file

@ -0,0 +1,55 @@
<template>
<div id="appSettings">
<h3>Additional features</h3>
<p class="uk-margin-small">
<label
><input v-model="IHIEnabled" class="uk-checkbox" type="checkbox" />
Enable IHI Slide Scan</label
>
</p>
<p class="uk-margin-small uk-width-xlarge">
Enabling IHI Slide Scan will add a new tab designed to simplify
acquiring blood smear tile scans. </br>
While this may be useful for other applications, it is primarily designed
for use in clinics acquiring blood smear samples using a 100x oil-immersion
objective, with a Raspberry Pi Camera v2.
</p>
</div>
</template>
<script>
// Export main app
export default {
name: "FeaturesSettings",
data: function() {
return {};
},
computed: {
IHIEnabled: {
get() {
return this.$store.state.globalSettings.IHIEnabled;
},
set(value) {
this.$store.commit("changeSetting", ["IHIEnabled", value]);
this.$root.$emit("globalSafeTogglePreview", value);
}
}
},
watch: {
IHIEnabled() {
console.log("Saving IHIEnabled setting");
this.setLocalStorageObj("IHIEnabled", this.IHIEnabled);
}
},
mounted() {
// Try loading settings from localStorage. If null, don't change.
this.IHIEnabled = this.getLocalStorageObj("IHIEnabled") || this.IHIEnabled;
}
};
</script>
<style lang="less"></style>

View file

@ -17,6 +17,19 @@
Display Display
</tabIcon> </tabIcon>
</li> </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> <li>
<tabIcon <tabIcon
@ -71,6 +84,16 @@
</div> </div>
</tabContent> </tabContent>
<tabContent
tab-i-d="features"
:require-connection="false"
:current-tab="currentTab"
>
<div class="settings-pane uk-padding-small">
<featuresSettings />
</div>
</tabContent>
<tabContent <tabContent
tab-i-d="camera" tab-i-d="camera"
:require-connection="true" :require-connection="true"
@ -116,6 +139,7 @@ import streamSettings from "./settingsComponents/streamSettings.vue";
import microscopeSettings from "./settingsComponents/microscopeSettings.vue"; import microscopeSettings from "./settingsComponents/microscopeSettings.vue";
import cameraSettings from "./settingsComponents/cameraSettings.vue"; import cameraSettings from "./settingsComponents/cameraSettings.vue";
import appSettings from "./settingsComponents/appSettings.vue"; import appSettings from "./settingsComponents/appSettings.vue";
import featuresSettings from "./settingsComponents/featuresSettings.vue";
import cameraStageMappingSettings from "./settingsComponents/cameraStageMappingSettings.vue"; import cameraStageMappingSettings from "./settingsComponents/cameraStageMappingSettings.vue";
// Import generic components // Import generic components
@ -132,6 +156,7 @@ export default {
microscopeSettings, microscopeSettings,
cameraStageMappingSettings, cameraStageMappingSettings,
appSettings, appSettings,
featuresSettings,
tabIcon, tabIcon,
tabContent tabContent
}, },
@ -157,9 +182,6 @@ export default {
// Custom UIkit CSS modifications // Custom UIkit CSS modifications
@import "../../assets/less/theme.less"; @import "../../assets/less/theme.less";
.settings-pane {
}
.settings-nav { .settings-nav {
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;

View file

@ -13,6 +13,7 @@ export default new Vuex.Store({
disableStream: false, disableStream: false,
autoGpuPreview: false, autoGpuPreview: false,
trackWindow: true, trackWindow: true,
IHIEnabled: false,
appTheme: "system" appTheme: "system"
}, },
activeStreams: {} activeStreams: {}