-
-
Save oeway/f59c1d1c49c94a831e5e21ba4c6111dd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<docs lang="markdown"> | |
[TODO: write documentation for this plugin.] | |
</docs> | |
<config lang="json"> | |
{ | |
"name": "PycroCam", | |
"type": "window", | |
"tags": [], | |
"ui": "", | |
"version": "0.1.2", | |
"api_version": "0.1.8", | |
"description": "A microscopy control plugin built on top of PycroManager", | |
"icon": "extension", | |
"inputs": null, | |
"outputs": null, | |
"env": "", | |
"requirements": [ | |
"https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.min.js", | |
"https://unpkg.com/buefy/dist/buefy.min.css", | |
"https://unpkg.com/buefy/dist/buefy.min.js", | |
"https://cdn.materialdesignicons.com/5.3.45/css/materialdesignicons.min.css", | |
"https://oeway.github.io/itk-vtk-viewer/itkVtkViewerCDN.js" | |
], | |
"dependencies": [], | |
"defaults": {"w": 20, "h": 10}, | |
"runnable": true | |
} | |
</config> | |
<script lang="javascript"> | |
api.registerCodec({ | |
name: 'itkimage', | |
decoder: itkVtkViewer.utils.convertToItkImage | |
}) | |
api.registerCodec({ | |
name: 'ndarray', | |
decoder: itkVtkViewer.utils.ndarrayToItkImage | |
}) | |
const app = new Vue({ | |
el: '#app', | |
data: { | |
mmcore: null, | |
viewer: null, | |
liveRunning: false, | |
binning: 1, | |
exposure: 100, | |
cameraDevice: null, | |
}, | |
methods: { | |
async init(mmcore){ | |
this.mmcore = mmcore; | |
this.cameraDevice = await this.mmcore.getCameraDevice() | |
this.exposure = await this.mmcore.getExposure() | |
this.binning = await this.mmcore.getProperty(this.cameraDevice, 'Binning') | |
this.updateImage() | |
}, | |
async createViewer(imageData, el) { | |
imageData = itkVtkViewer.utils.vtkITKHelper.convertItkToVtkImage(imageData) | |
// clear container | |
el.innerHTML = ""; | |
const toolbar = document.createElement('div') | |
el.appendChild(toolbar) | |
const view = document.createElement('div') | |
el.appendChild(view) | |
const dims = imageData.getDimensions() | |
const is2D = dims.length === 2 || (dims.length === 3 && dims[2] === 1) | |
const viewer = itkVtkViewer.createViewer(view, { | |
image: imageData, | |
pointSets: null, | |
geometries: null, | |
use2D: is2D, | |
rotate: false, | |
uiContainer: toolbar | |
}) | |
return viewer | |
}, | |
async updateImage(){ | |
if(!this.mmcore){ | |
api.alert('No mmcore api provided.') | |
return | |
} | |
let img; | |
if(this.liveRunning){ | |
img = await this.mmcore.getImage() | |
// if no image available, return | |
if(!img) return; | |
} | |
else{ | |
img = await this.mmcore.snapImage() | |
} | |
if(!this.viewer){ | |
const elm = document.getElementById('viewer') | |
this.viewer = await this.createViewer(img, elm) | |
} | |
else{ | |
this.viewer.setImage(itkVtkViewer.utils.vtkITKHelper.convertItkToVtkImage(img)) | |
} | |
if(this.liveRunning){ | |
this.updateImage() | |
} | |
}, | |
async toggleLive(){ | |
this.liveRunning = !this.liveRunning | |
if(this.liveRunning){ | |
this.mmcore.startContinuousSequenceAcquisition(0) | |
this.updateImage() | |
} | |
else{ | |
this.mmcore.stopSequenceAcquisition() | |
} | |
}, | |
async showDevicePropertyBrowser(){ | |
const browser = await api.showDialog({src: "https://gist.github.com/oeway/d07f14fca4d9ab1febbde200d0369bf2", data:{'mmcore': this.mmcore}}) | |
browser.on('property_changed', (item)=>{ | |
if(item.device === this.cameraDevice && item.prop === 'Exposure'){ | |
this.exposure = item.value | |
} | |
else if(item.device === this.cameraDevice && item.prop === 'Binning'){ | |
this.binning = item.value | |
} | |
}) | |
} | |
} | |
}) | |
class ImJoyPlugin { | |
async setup() { | |
} | |
async run(ctx) { | |
await app.init(ctx.data.mmcore) | |
} | |
} | |
api.export(new ImJoyPlugin()) | |
</script> | |
<window lang="html"> | |
<div id="app"> | |
<section> | |
<div class="buttons"> | |
<b-button type="is-primary" @click="updateImage()" :loading="liveRunning">Snap</b-button> | |
<b-button :type="liveRunning?'is-danger': 'is-primary is-light'" | |
@click="toggleLive()">{{liveRunning?'Stop Live': 'Start Live'}}</b-button> | |
Exposure: <b-field> | |
<b-input placeholder="Exposure" | |
size="is-small" | |
@input="mmcore.setExposure(parseFloat(exposure))" | |
v-model="exposure" | |
type="number" | |
min="0" | |
max="10000"> | |
</b-input> | |
</b-field> | |
Binning: <b-field> | |
<b-select placeholder="Binning" | |
size="is-small" | |
@input="mmcore.setProperty(cameraDevice, 'Binning', binning)" | |
v-model="binning"> | |
<option :value="1">1</option> | |
<option :value="2">2</option> | |
<option :value="4">4</option> | |
<option :value="8">8</option> | |
<option :value="16">16</option> | |
</b-select> | |
</b-field> | |
<b-button size="is-small" @click="showDevicePropertyBrowser()">Device Properties</b-button> | |
</div> | |
</section> | |
<section id="viewer-section"> | |
<div id="viewer" class="viewer-container"> | |
</div> | |
</section> | |
</div> | |
</window> | |
<style lang="css"> | |
#app{ | |
margin-top: 2px; | |
} | |
#viewer-section{ | |
display: table; | |
margin-top: 5px; | |
max-width: calc( 100vh - 32px ); | |
} | |
.viewer-container{ | |
display: table-cell; | |
position: relative; | |
} | |
.field { | |
margin-bottom: .2rem!important; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment