cubevis 0.5.27__py3-none-any.whl → 0.5.29__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cubevis might be problematic. Click here for more details.
- cubevis/__icons__/trash.png +0 -0
- cubevis/__icons__/trash.svg +20 -0
- cubevis/__icons__/trash_full.png +0 -0
- cubevis/__icons__/trash_full.svg +35 -0
- cubevis/__icons__/trash_full_raw.png +0 -0
- cubevis/__js__/cubevisjs.min.js +5 -5
- cubevis/__version__.py +1 -1
- cubevis/bokeh/sources/_data_pipe.py +128 -2
- cubevis/toolbox/_cube.py +70 -2
- {cubevis-0.5.27.dist-info → cubevis-0.5.29.dist-info}/METADATA +1 -1
- {cubevis-0.5.27.dist-info → cubevis-0.5.29.dist-info}/RECORD +13 -8
- {cubevis-0.5.27.dist-info → cubevis-0.5.29.dist-info}/WHEEL +0 -0
- {cubevis-0.5.27.dist-info → cubevis-0.5.29.dist-info}/licenses/LICENSE +0 -0
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="512" height="512" fill="#f5f5f5"/>
|
|
3
|
+
|
|
4
|
+
<!-- Trash can body (tapered) -->
|
|
5
|
+
<path d="M 160 200 L 352 200 L 332 440 Q 332 460 312 460 L 200 460 Q 180 460 180 440 L 160 200 Z"
|
|
6
|
+
fill="none" stroke="black" stroke-width="24" stroke-linejoin="round"/>
|
|
7
|
+
|
|
8
|
+
<!-- Trash can lid -->
|
|
9
|
+
<rect x="120" y="160" width="272" height="40" rx="20" ry="20"
|
|
10
|
+
fill="none" stroke="black" stroke-width="24"/>
|
|
11
|
+
|
|
12
|
+
<!-- Handle -->
|
|
13
|
+
<rect x="200" y="120" width="112" height="40" rx="20" ry="20"
|
|
14
|
+
fill="none" stroke="black" stroke-width="24"/>
|
|
15
|
+
|
|
16
|
+
<!-- Vertical indents -->
|
|
17
|
+
<line x1="220" y1="220" x2="210" y2="420" stroke="black" stroke-width="16" stroke-linecap="round"/>
|
|
18
|
+
<line x1="256" y1="220" x2="256" y2="420" stroke="black" stroke-width="16" stroke-linecap="round"/>
|
|
19
|
+
<line x1="292" y1="220" x2="302" y2="420" stroke="black" stroke-width="16" stroke-linecap="round"/>
|
|
20
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="512" height="512" fill="#f5f5f5"/>
|
|
3
|
+
|
|
4
|
+
<!-- Trash can body (tapered) -->
|
|
5
|
+
<path d="M 160 200 L 352 200 L 332 440 Q 332 460 312 460 L 200 460 Q 180 460 180 440 L 160 200 Z"
|
|
6
|
+
fill="none" stroke="black" stroke-width="24" stroke-linejoin="round"/>
|
|
7
|
+
|
|
8
|
+
<!-- Trash can lid (tilted open) -->
|
|
9
|
+
<ellipse cx="150" cy="140" rx="136" ry="20"
|
|
10
|
+
transform="rotate(-15 150 140)"
|
|
11
|
+
fill="none" stroke="black" stroke-width="24"/>
|
|
12
|
+
|
|
13
|
+
<!-- Handle (on tilted lid) -->
|
|
14
|
+
<rect x="105" y="100" width="112" height="40" rx="20" ry="20"
|
|
15
|
+
transform="rotate(-15 161 120)"
|
|
16
|
+
fill="black" stroke="none"/>
|
|
17
|
+
|
|
18
|
+
<!-- Vertical indents -->
|
|
19
|
+
<line x1="220" y1="220" x2="210" y2="420" stroke="black" stroke-width="16" stroke-linecap="round"/>
|
|
20
|
+
<line x1="256" y1="220" x2="256" y2="420" stroke="black" stroke-width="16" stroke-linecap="round"/>
|
|
21
|
+
<line x1="292" y1="220" x2="302" y2="420" stroke="black" stroke-width="16" stroke-linecap="round"/>
|
|
22
|
+
|
|
23
|
+
<!-- Overflow items -->
|
|
24
|
+
<circle cx="300" cy="120" r="16" fill="none" stroke="black" stroke-width="12"/>
|
|
25
|
+
<rect x="340" y="100" width="32" height="32" rx="8" fill="none" stroke="black" stroke-width="12"/>
|
|
26
|
+
<circle cx="380" cy="80" r="12" fill="none" stroke="black" stroke-width="10"/>
|
|
27
|
+
<rect x="320" y="60" width="24" height="24" rx="6" fill="none" stroke="black" stroke-width="10"/>
|
|
28
|
+
|
|
29
|
+
<!-- Additional overflow down the right side -->
|
|
30
|
+
<circle cx="390" cy="140" r="14" fill="none" stroke="black" stroke-width="10"/>
|
|
31
|
+
<rect x="400" y="170" width="28" height="28" rx="7" fill="none" stroke="black" stroke-width="10"/>
|
|
32
|
+
<circle cx="420" cy="210" r="10" fill="none" stroke="black" stroke-width="8"/>
|
|
33
|
+
<rect x="410" y="240" width="20" height="20" rx="5" fill="none" stroke="black" stroke-width="8"/>
|
|
34
|
+
<circle cx="430" cy="270" r="8" fill="none" stroke="black" stroke-width="6"/>
|
|
35
|
+
</svg>
|
|
Binary file
|
cubevis/__js__/cubevisjs.min.js
CHANGED
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
}
|
|
43
43
|
})
|
|
44
44
|
({
|
|
45
|
-
"97397933dc": function _(a,t,e,o,c){o();const i=a("tslib"),n=a("
|
|
46
|
-
"
|
|
45
|
+
"97397933dc": function _(a,t,e,o,c){o();const i=a("tslib"),n=a("33e5a43d86");c("DataPipe",n.DataPipe);const s=a("2889e0dd45");c("ImagePipe",s.ImagePipe);const p=a("de65005924");c("ImageDataSource",p.ImageDataSource);const r=a("02e3c3e46c");c("SpectraDataSource",r.SpectraDataSource);const S=a("64b16deff9");c("UpdatableDataSource",S.UpdatableDataSource);const d=a("b6ae454f0d");c("WcsTicks",d.WcsTicks);const D=a("cb7d28d6b3");c("DragTool",D.DragTool);const T=a("01959f25a3");c("CBResetTool",T.CBResetTool);const l=a("e3901fa9f2");c("serialize",l.serialize),c("deserialize",l.deserialize);const u=a("9f961622ce");c("TipButton",u.TipButton);const b=a("ca4c845905");c("Tip",b.Tip);const g=a("50a1e32f01");c("SharedDict",g.SharedDict);const f=a("b55081402e");c("EditSpan",f.EditSpan);const E=a("9144bfc7a5");c("EvTextInput",E.EvTextInput);const I=a("74e0abef8a");c("EvPolyAnnotation",I.EvPolyAnnotation);const P=i.__importStar(a("15b954190c"));e.find=P;(0,a("@bokehjs/base").register_models)({DataPipe:n.DataPipe,ImagePipe:s.ImagePipe,ImageDataSource:p.ImageDataSource,SpectraDataSource:r.SpectraDataSource,UpdatableDataSource:S.UpdatableDataSource,WcsTicks:d.WcsTicks,DragTool:D.DragTool,CBResetTool:T.CBResetTool,Tip:b.Tip,TipButton:u.TipButton,SharedDict:g.SharedDict,EditSpan:f.EditSpan,EvTextInput:E.EvTextInput,EvPolyAnnotation:I.EvPolyAnnotation})},
|
|
46
|
+
"33e5a43d86": function _(e,s,t,i,n){var o;i();const a=e("@bokehjs/models/sources/data_source"),c=e("e3901fa9f2"),r=e("@bokehjs/core/util/callbacks");class d extends a.DataSource{constructor(e){super(e),this.send_queue={},this.connection_queue=[],this.pending={},this.incoming_callbacks={},this.session_id=casalib.object_id(this)}checkSessionConflict(){try{if("undefined"==typeof Storage)return console.warn("localStorage not available, skipping session conflict detection"),!0;const e=localStorage.getItem(this.session_storage_key);if(e){const s=JSON.parse(e);if(s.sessionId!==this.session_id&&Date.now()-s.timestamp<12e4){const e=`CubeVis DataPipe (${this.instance_key}) is already running in another browser window or tab.\n\nPlease close other instances and refresh this page, or\nclose this window to continue using the other instance.`;return alert(e),window.opener||1===window.history.length?window.close():window.location.href="about:blank",!1}}return this.updateSessionHeartbeat(),!0}catch(e){return console.warn("Session conflict detection failed:",e),!0}}updateSessionHeartbeat(){try{"undefined"!=typeof Storage&&localStorage.setItem(this.session_storage_key,JSON.stringify({sessionId:this.session_id,timestamp:Date.now(),instanceKey:this.instance_key}))}catch(e){console.warn("Session heartbeat update failed:",e)}}startHeartbeat(){this.heartbeat_interval=window.setInterval((()=>{this.updateSessionHeartbeat()}),3e4)}stopHeartbeat(){this.heartbeat_interval&&(clearInterval(this.heartbeat_interval),this.heartbeat_interval=void 0)}cleanupSession(){try{if("undefined"!=typeof Storage){const e=localStorage.getItem(this.session_storage_key);if(e){JSON.parse(e).sessionId===this.session_id&&localStorage.removeItem(this.session_storage_key)}}}catch(e){console.warn("Session cleanup failed:",e)}this.stopHeartbeat()}handleSessionConflictMessage(e){console.error("Session conflict detected by server:",e);let s="Session conflict detected by server.";"session_conflict"===e.type?s=e.error||s:"session_corruption"===e.type&&(s=`Session corruption detected.\nExpected: ${e.expected}\nReceived: ${e.received}`),alert(s+"\n\nThis window will be closed to prevent data corruption."),this.cleanupSession();const t=new CustomEvent("cubevis_session_conflict",{detail:{message:e,sessionId:this.session_id}});window.dispatchEvent(t),setTimeout((()=>{window.opener||1===window.history.length?window.close():window.location.href="about:blank"}),2e3)}generateInstanceKey(){return`${this.address[0]}_${this.address[1]}`}initialize(){if(super.initialize(),this.instance_key=this.generateInstanceKey(),this.session_storage_key=`cubevis_datapipe_${this.instance_key}`,!this.checkSessionConflict())return;let e=`ws://${this.address[0]}:${this.address[1]}`;console.log("datapipe url:",e);var s=void 0;document.shutdown_in_progress_=!1;var t=()=>{void 0!==this.websocket&&this.websocket.close(),this.websocket=new WebSocket(e),this.websocket.binaryType="arraybuffer",this.websocket.addEventListener("error",(e=>{console.log("error encountered:",e)})),this.websocket.onmessage=e=>{if("string"==typeof e.data||e.data instanceof String){let s=(0,c.deserialize)(e.data);if("id"in s&&"direction"in s&&"message"in s){let{id:e,message:t,direction:i}=s;if("error"===i&&("session_conflict"===e||e===this.session_id)&&t&&("session_conflict"===t.type||"session_corruption"===t.type||"close_duplicate"===t.action))return void this.handleSessionConflictMessage(t);if(void 0===t&&console.log("Error, event failure",s),"j2p"==i)if(e in this.pending){let{cb:i}=this.pending[e];if(delete this.pending[e],e in this.send_queue&&this.send_queue[e].length>0){let{cb:s,msg:t}=this.send_queue[e].shift();this.pending[e]={cb:s},this.websocket.send((0,c.serialize)(t))}void 0===t?console.log("DROPPING ERROR FOR NOW (maybe need error callbacks)",s):i(t)}else console.log("message received but could not find id");else if(e in this.incoming_callbacks){let s=this.incoming_callbacks[e](t);this.websocket.send((0,c.serialize)({id:e,direction:i,message:s,session:this.session_id}))}}else console.log(`datapipe received message without one of 'id', 'message' or 'direction': ${s}`)}else console.log("datapipe received binary data",e.data.byteLength,"bytes")},this.websocket.onopen=()=>{for(s?0==s.connected&&console.log(`connection reestablished at ${new Date}`):(this.websocket.send((0,c.serialize)({id:"initialize",direction:"j2p",session:this.session_id})),this.startHeartbeat()),s=new casalib.ReconnectState;this.connection_queue.length>0;){let e=this.connection_queue.shift();this.send.apply(e[0],e[1])}},this.websocket.onclose=()=>{if(s&&1==s.connected&&(console.log(`connection lost at ${new Date}`),s.connected=!1,!document.shutdown_in_progress_)){console.log(`connection lost at ${new Date}`);var e=s;function i(n){0==s.connected&&(console.log(`${n+1}\treconnection attempt ${new Date}`),t(),e.backoff(),e.retries>0?setTimeout(i,e.timeout,n+1):0==s.connected&&console.log(`aborting reconnection after ${n} attempts ${new Date}`))}i(0)}}};window.addEventListener("beforeunload",(()=>{this.cleanupSession()})),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState?this.stopHeartbeat():"visible"===document.visibilityState&&(this.updateSessionHeartbeat(),this.startHeartbeat())})),t();(()=>{null!=this.init_script&&(0,r.execute)(this.init_script,this)})()}register(e,s){this.incoming_callbacks[e]=s}send(e,s,t,i=!1){let n={id:e,message:s,direction:"j2p",session:this.session_id};if(!this.websocket||e in this.pending)if(e in this.send_queue)if("boolean"==typeof i&&i&&this.send_queue[e].length>0)this.send_queue[e][0].msg=n,this.send_queue[e][0].cb=t;else if("function"==typeof i&&this.send_queue[e].length>0){let o=!1;for(const a of this.send_queue[e])i(a.msg.message)&&(a.msg=n,a.cb=t,o=!0);o||this.send_queue[e].push({cb:t,msg:n})}else this.send_queue[e].push({cb:t,msg:n});else this.send_queue[e]=[{cb:t,msg:n}];else if(this.websocket.readyState===WebSocket.CONNECTING)this.connection_queue.push([this,[e,s,t]]);else if(e in this.send_queue&&this.send_queue[e].length>0){this.send_queue[e].push({cb:t,msg:n});{let{cb:r,msg:d}=this.send_queue[e].shift();if(this.pending[e]={cb:r},this.websocket.readyState===WebSocket.OPEN)this.websocket.send((0,c.serialize)(d));else{let l=20,h=this;function u(){h.websocket.readyState===WebSocket.OPEN?h.websocket.send((0,c.serialize)(d)):(l-=1,l>0&&setTimeout(u,3e3))}setTimeout(u,3e3)}}}else if(this.websocket.readyState===WebSocket.OPEN)this.pending[e]={cb:t},this.websocket.send((0,c.serialize)(n));else{let b=20,_=this;function g(){_.websocket.readyState===WebSocket.OPEN?(_.pending[e]={cb:t},_.websocket.send((0,c.serialize)(n))):(b-=1,b>0&&setTimeout(g,3e3))}setTimeout(g,3e3)}}}t.DataPipe=d,o=d,d.__name__="DataPipe",d.__module__="cubevis.bokeh.sources._data_pipe",o.define((({Any:e,Tuple:s,String:t,Number:i})=>({init_script:[e,null],address:[s(t,i)]})))},
|
|
47
47
|
"e3901fa9f2": function _(e,r,s,i,o){i();const l=e("@bokehjs/base"),a=e("@bokehjs/core/resolvers"),t=e("@bokehjs/core/serialization/deserializer"),n=e("@bokehjs/core/serialization/serializer"),{deserialize:c}=new class{constructor(){this.resolver=new a.ModelResolver(l.default_resolver),this.deserializer=new t.Deserializer(this.resolver),this.deserialize=e=>{try{return this.deserializer.decode(JSON.parse(e))}catch(r){return console.group("deserialize error"),console.log(e),console.log(r),console.groupEnd(),{}}}}};s.deserialize=c;const{serialize:z}=new class{constructor(){this.serializer=new n.Serializer,this.serialize=e=>JSON.stringify(this.serializer.encode(e))}};s.serialize=z},
|
|
48
|
-
"2889e0dd45": function _(i,e,s,t,n){var a;t();const o=i("@bokehjs/models/sources/column_data_source"),d=i("
|
|
48
|
+
"2889e0dd45": function _(i,e,s,t,n){var a;t();const o=i("@bokehjs/models/sources/column_data_source"),d=i("33e5a43d86");class r extends d.DataPipe{constructor(i){super(i),this.position={},this._wcs=null}initialize(){super.initialize(),this.fits_header_json&&(this._wcs=new casalib.coordtxl.WCSTransform(new casalib.coordtxl.MapKeywordProvider(JSON.parse(this.fits_header_json))))}channel(i,e,s){this.position[s]={index:i};let t={action:"channel",index:i,id:s};super.send(this.dataid,t,(i=>{null!=this._histogram_source&&"hist"in i&&"top"in i.hist&&"bottom"in i.hist&&"left"in i.hist&&"right"in i.hist&&(this._histogram_source.data=i.hist),e(i)}))}spectrum(i,e,s,t=!1){let n={action:"spectrum",index:i,id:s};super.send(this.dataid,n,e,t)}adjust_colormap(i,e,s,t,n=!1){const a={action:"adjust-colormap",bounds:i,transfer:e,id:t};super.send(this.dataid,a,s,n)}refresh(i,e,s=[0,0]){let{index:t}=e in this.position?this.position[e]:{index:s};if(2===t.length){let s={action:"channel",index:t,id:e};super.send(this.dataid,s,i)}else if(3===t.length){let s={action:"spectrum",index:t,id:e};super.send(this.dataid,s,i)}}wcs(){return this._wcs}}s.ImagePipe=r,a=r,r.__name__="ImagePipe",r.__module__="cubevis.bokeh.sources._image_pipe",a.define((({Number:i,Nullable:e,String:s,Tuple:t,Ref:n})=>({dataid:[s],shape:[t(i,i,i,i)],fits_header_json:[e(s),null],_histogram_source:[e(n(o.ColumnDataSource)),null]})))},
|
|
49
49
|
"de65005924": function _(s,a,t,c,i){var o;c();const e=s("@bokehjs/models/sources/column_data_source"),n=s("@bokehjs/core/util/string"),u=s("2889e0dd45"),h=s("@bokehjs/core/util/callbacks");class r extends e.ColumnDataSource{constructor(s){super(s),this.imid=(0,n.uuid4)()}_mask_contour(s){const a=casalib.d3.contours().size(this.image_source.shape.slice(0,2)).thresholds([1])(s[0])[0].coordinates.map((s=>s.map((s=>s.reduce(((s,a)=>(s[0].push(a[0]),s[1].push(a[1]),s)),[[],[]])))));return{xs:[a.map((s=>s.map((s=>s[0]))))],ys:[a.map((s=>s.map((s=>s[1]))))]}}initialize(){if(super.initialize(),null!=this._mask_contour_source&&"msk"in this.data&&this.data.msk.length>0&&this.data.msk[0].length>0){const s=this.data.msk;this._mask_contour_source.data=this._mask_contour(s)}void 0===this.last_chan&&(this.last_chan=[this.cur_chan[0].valueOf(),this.cur_chan[1].valueOf()]);(()=>{null!=this.init_script&&(0,h.execute)(this.init_script,this)})()}channel(s,a=0,t){this.image_source.channel([a,s],(c=>{void 0!==c&&void 0!==c.chan||console.log("ImageDataSource ERROR ENCOUNTERED <1>",c),this.last_chan=[this.cur_chan[0].valueOf(),this.cur_chan[1].valueOf()],this.cur_chan=[a,s],null!=this._mask_contour_source&&"chan"in c&&"msk"in c.chan&&(c.msk_contour=this._mask_contour(c.chan.msk),this._mask_contour_source.data=c.msk_contour),t&&t(c),this.data=c.chan}),this.imid)}adjust_colormap(s,a,t){this.image_source.adjust_colormap(s,a,t,this.imid,!0)}signal_change(){this.change.emit()}refresh(s){this.image_source.refresh((a=>{void 0!==a&&void 0!==a.chan||console.log("ImageDataSource ERROR ENCOUNTERED <2>",a),null!=this._mask_contour_source&&"chan"in a&&"msk"in a.chan&&(a.msk_contour=this._mask_contour(a.chan.msk),this._mask_contour_source.data=a.msk_contour),s&&s(a),this.data=a.chan}),this.imid,[0,0])}wcs(){return this.image_source.wcs()}}t.ImageDataSource=r,o=r,r.__name__="ImageDataSource",r.__module__="cubevis.bokeh.sources._image_data_source",o.define((({Tuple:s,Number:a,Ref:t,Nullable:c,Any:i})=>({init_script:[i,null],image_source:[t(u.ImagePipe)],_mask_contour_source:[c(t(e.ColumnDataSource)),null],num_chans:[s(a,a)],cur_chan:[s(a,a)]})))},
|
|
50
50
|
"02e3c3e46c": function _(e,s,i,t,r){var a;t();const c=e("@bokehjs/models/sources/column_data_source"),u=e("@bokehjs/core/util/string"),o=e("2889e0dd45");class _ extends c.ColumnDataSource{constructor(e){super(e),this.imid=(0,u.uuid4)()}initialize(){super.initialize()}spectra(e,s,i=0,t=!1){this.image_source.spectrum([e,s,i],(e=>this.data=e.spectrum),this.imid,t)}refresh(){this.image_source.refresh((e=>this.data=e.spectrum),this.imid,[0,0,0])}}i.SpectraDataSource=_,a=_,_.__name__="SpectraDataSource",_.__module__="cubevis.bokeh.sources._spectra_data_source",a.define((({Ref:e})=>({image_source:[e(o.ImagePipe)]})))},
|
|
51
|
-
"64b16deff9": function _(e,s,i,t,a){var n;t();const u=e("@bokehjs/models/sources/column_data_source"),l=e("
|
|
51
|
+
"64b16deff9": function _(e,s,i,t,a){var n;t();const u=e("@bokehjs/models/sources/column_data_source"),l=e("33e5a43d86"),o=e("@bokehjs/core/util/callbacks");class c extends u.ColumnDataSource{constructor(e){super(e)}send(e,s){this.pipe.send(this.session_id.valueOf(),{action:"callback",message:e},(e=>{s("result"in e?e.result:{error:`expected to find a "result" in "${e}"`,msg:e})}))}initialize(){super.initialize();(()=>{null!=this.js_init&&(0,o.execute)(this.js_init,this)})()}}i.UpdatableDataSource=c,n=c,c.__name__="UpdatableDataSource",c.__module__="cubevis.bokeh.sources._updatable_data_source",n.define((({Ref:e,Any:s,String:i})=>({js_init:[s,null],js_update:[s,null],pipe:[e(l.DataPipe)],session_id:[i]})))},
|
|
52
52
|
"b6ae454f0d": function _(s,i,o,t,e){var r;t();const a=s("@bokehjs/models/formatters/tick_formatter"),c=s("de65005924");class l extends a.TickFormatter{constructor(s){super(s),this._axis=null,this._coord="world"}initialize(){super.initialize(),"x"==this.axis||"X"==this.axis||"y"==this.axis||"Y"==this.axis?this._axis="x"==this.axis||"X"==this.axis?"x":"y":console.log("ERROR: WcsTicks formatter created with invalid axis:",this.axis)}doFormat(s){const i=[];if(this._axis&&this.image_source.wcs()&&"world"==this._coord)for(let o=0,t=s.length;o<t;o++)if("x"==this._axis){const t=new casalib.coordtxl.Point2D(Number(s[o]),0);this.image_source.wcs().imageToWorldCoords(t,!1),i.push(new casalib.coordtxl.WorldCoords(t.getX(),t.getY()).format(2e3)[0])}else{const t=new casalib.coordtxl.Point2D(0,Number(s[o]));this.image_source.wcs().imageToWorldCoords(t,!1),i.push(new casalib.coordtxl.WorldCoords(t.getX(),t.getY()).format(2e3)[1])}else for(let o=0,t=s.length;o<t;o++)i.push(""+s[o]);return i}coordinates(s){return s!=this._coord&&("world"!=s&&"pixel"!=s||(this._coord=s)),this._coord}}o.WcsTicks=l,r=l,l.__name__="WcsTicks",l.__module__="cubevis.bokeh.format._wcs_ticks",r.define((({Ref:s,String:i})=>({axis:[i],image_source:[s(c.ImageDataSource)]})))},
|
|
53
53
|
"cb7d28d6b3": function _(i,e,t,o,s){var d;o();const l=i("@bokehjs/models/tools/gestures/gesture_tool"),r=i("949501ff1c"),_=i("15b954190c"),m=i("@bokehjs/core/util/callbacks");class n extends l.GestureToolView{_pan_start(i){var e;null===(e=this.model.document)||void 0===e||e.interactive_start(this.plot_view.model);const t=(0,_.px_from_sx)(this.plot_view,i.sx),o=(0,_.py_from_sy)(this.plot_view,i.sy),s=(0,_.dx_from_px)(this.plot_view,t),d=(0,_.dy_from_py)(this.plot_view,o),{start:l}=this.model;l?(0,m.execute)(l,this.model,{sx:t,sy:o,x:s,y:d,delta_x:i.dx,delta_y:-i.dy,shift:"modifiers"in i?i.modifiers.shift:void 0,ctrl:"modifiers"in i?i.modifiers.ctrl:void 0,alt:"modifiers"in i?i.modifiers.alt:void 0}):this.model.trigger_event(new r.DragStart(t,o,s,d,i.dx,-i.dy,i.modifiers))}_pan(i){var e;null===(e=this.model.document)||void 0===e||e.interactive_start(this.plot_view.model);const t=(0,_.px_from_sx)(this.plot_view,i.sx),o=(0,_.py_from_sy)(this.plot_view,i.sy),s=(0,_.dx_from_px)(this.plot_view,t),d=(0,_.dy_from_py)(this.plot_view,o),{move:l}=this.model;l?(0,m.execute)(l,this.model,{sx:t,sy:o,x:s,y:d,delta_x:i.dx,delta_y:-i.dy,shift:"modifiers"in i?i.modifiers.shift:void 0,ctrl:"modifiers"in i?i.modifiers.ctrl:void 0,alt:"modifiers"in i?i.modifiers.alt:void 0}):this.model.trigger_event(new r.Drag(t,o,s,d,i.dx,-i.dy,i.modifiers))}_pan_end(i){const e=(0,_.px_from_sx)(this.plot_view,i.sx),t=(0,_.py_from_sy)(this.plot_view,i.sy),o=(0,_.dx_from_px)(this.plot_view,e),s=(0,_.dy_from_py)(this.plot_view,t),{end:d}=this.model;d?(0,m.execute)(d,this.model,{sx:e,sy:t,x:o,y:s,delta_x:i.dx,delta_y:-i.dy,shift:"modifiers"in i?i.modifiers.shift:void 0,ctrl:"modifiers"in i?i.modifiers.ctrl:void 0,alt:"modifiers"in i?i.modifiers.alt:void 0}):this.model.trigger_event(new r.DragEnd(e,t,o,s,i.dx,-i.dy,i.modifiers))}}t.DragToolView=n,n.__name__="DragToolView";class a extends l.GestureTool{constructor(i){super(i),this.tool_name="Drag",this.event_type="pan",this.default_order=10}}t.DragTool=a,d=a,a.__name__="DragTool",a.__module__="cubevis.bokeh.tools._drag_tool",d.prototype.default_view=n,d.define((({Any:i,Nullable:e})=>({start:[e(i),null],move:[e(i),null],end:[e(i),null]})))},
|
|
54
54
|
"949501ff1c": function _(e,t,a,s,n){s();const _=e("@bokehjs/core/bokeh_events");class r extends _.Pan{}a.Drag=r,r.__name__="Drag";class l extends _.PanStart{constructor(e,t,a,s,n,_,r){super(e,t,a,s,r),this.delta_x=n,this.delta_y=_}get event_values(){const{delta_x:e,delta_y:t}=this;return Object.assign(Object.assign({},super.event_values),{delta_x:e,delta_y:t})}}a.DragStart=l,l.__name__="DragStart";class d extends _.PanEnd{constructor(e,t,a,s,n,_,r){super(e,t,a,s,r),this.delta_x=n,this.delta_y=_}get event_values(){const{delta_x:e,delta_y:t}=this;return Object.assign(Object.assign({},super.event_values),{delta_x:e,delta_y:t})}}a.DragEnd=d,d.__name__="DragEnd"},
|
|
@@ -60,4 +60,4 @@
|
|
|
60
60
|
"b55081402e": function _(e,n,t,_,s){var a;_();const o=e("@bokehjs/models/annotations/span"),p=e("@bokehjs/core/bokeh_events");class r extends o.SpanView{on_pan_start(e){const n=super.on_pan_start(e);return this.model.trigger_event(new p.LODStart),n}on_pan(e){super.on_pan(e)}on_pan_end(e){super.on_pan_end(e),this.model.trigger_event(new p.LODEnd)}}t.EditSpanView=r,r.__name__="EditSpanView";class d extends o.Span{constructor(e){super(e)}}t.EditSpan=d,a=d,d.__name__="EditSpan",d.__module__="cubevis.bokeh.models._edit_span",a.prototype.default_view=r},
|
|
61
61
|
"9144bfc7a5": function _(e,t,s,n,r){var i;n();const l=e("@bokehjs/models/widgets/text_input"),o=e("@bokehjs/core/dom"),u=e("@bokehjs/core/bokeh_events");class _ extends l.TextInputView{stylesheets(){return[...super.stylesheets(),new o.InlineStyleSheet(".bk-input-prefix { padding: 0 var(--padding-vertical); }")]}connect_signals(){super.connect_signals(),this.el.addEventListener("mouseenter",(e=>{this.model.trigger_event(new u.MouseEnter(e.screenX,e.screenY,e.x,e.y,{shift:e.shiftKey,ctrl:e.ctrlKey,alt:e.altKey}))})),this.el.addEventListener("mouseleave",(e=>{this.model.trigger_event(new u.MouseLeave(e.screenX,e.screenY,e.x,e.y,{shift:e.shiftKey,ctrl:e.ctrlKey,alt:e.altKey}))}))}render(){super.render()}}s.EvTextInputView=_,_.__name__="EvTextInputView";class c extends l.TextInput{constructor(e){super(e)}}s.EvTextInput=c,i=c,c.__name__="EvTextInput",c.__module__="cubevis.bokeh.models._ev_text_input",i.prototype.default_view=_},
|
|
62
62
|
"74e0abef8a": function _(e,t,n,s,o){var i;s();const r=e("@bokehjs/models/annotations/poly_annotation"),a=e("@bokehjs/core/bokeh_events");class _ extends r.PolyAnnotationView{on_enter(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.MouseEnter(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt}),o=super.on_enter(e);return this.model.trigger_event(s),o}on_leave(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.MouseLeave(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt});super.on_leave(e),this.model.trigger_event(s)}on_pan_start(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.PanStart(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt}),o=super.on_pan_start(e);return this.model.trigger_event(s),o}on_pan_end(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.PanEnd(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt});super.on_pan_end(e),this.model.trigger_event(s)}on_pan(e){super.on_pan(e);const t=new a.RangesUpdate(e.sx,e.sx+e.dx,e.sy,e.sy+e.dy);this.model.trigger_event(t)}}n.EvPolyAnnotationView=_,_.__name__="EvPolyAnnotationView";class l extends r.PolyAnnotation{constructor(e){super(e)}}n.EvPolyAnnotation=l,i=l,l.__name__="EvPolyAnnotation",l.__module__="cubevis.bokeh.annotations._ev_poly_annotation",i.prototype.default_view=_},
|
|
63
|
-
}, "97397933dc", {"index":"97397933dc","src/bokeh/sources/data_pipe":"
|
|
63
|
+
}, "97397933dc", {"index":"97397933dc","src/bokeh/sources/data_pipe":"33e5a43d86","src/bokeh/util/conversions":"e3901fa9f2","src/bokeh/sources/image_pipe":"2889e0dd45","src/bokeh/sources/image_data_source":"de65005924","src/bokeh/sources/spectra_data_source":"02e3c3e46c","src/bokeh/sources/updatable_data_source":"64b16deff9","src/bokeh/format/wcs_ticks":"b6ae454f0d","src/bokeh/tools/drag_tool":"cb7d28d6b3","src/bokeh/events":"949501ff1c","src/bokeh/util/find":"15b954190c","src/bokeh/tools/cbreset_tool":"01959f25a3","src/bokeh/models/tip_button":"9f961622ce","src/bokeh/models/tip":"ca4c845905","src/bokeh/models/shared_dict":"50a1e32f01","src/bokeh/models/edit_span":"b55081402e","src/bokeh/models/ev_text_input":"9144bfc7a5","src/bokeh/annotations/ev_poly_annotation":"74e0abef8a"}, {});});
|
cubevis/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.5.
|
|
1
|
+
__version__ = '0.5.29'
|
|
@@ -34,6 +34,8 @@ import inspect
|
|
|
34
34
|
import threading
|
|
35
35
|
import asyncio
|
|
36
36
|
import traceback
|
|
37
|
+
import time
|
|
38
|
+
import json
|
|
37
39
|
|
|
38
40
|
from bokeh.models.sources import DataSource
|
|
39
41
|
from bokeh.util.compiler import TypeScript
|
|
@@ -69,6 +71,10 @@ class DataPipe(DataSource):
|
|
|
69
71
|
|
|
70
72
|
address = Tuple( String, Int, help="two integer sequence representing the address and port to use for the websocket" )
|
|
71
73
|
|
|
74
|
+
# Class-level session tracking to prevent multiple connections
|
|
75
|
+
_active_sessions = {} # session_id -> {'websocket': ws, 'timestamp': time, 'datapipe': instance}
|
|
76
|
+
_session_lock = threading.Lock()
|
|
77
|
+
|
|
72
78
|
__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
|
|
73
79
|
|
|
74
80
|
def __init__( self, *args, abort=None, **kwargs ):
|
|
@@ -115,6 +121,73 @@ class DataPipe(DataSource):
|
|
|
115
121
|
return result
|
|
116
122
|
return None
|
|
117
123
|
|
|
124
|
+
@classmethod
|
|
125
|
+
async def __is_websocket_alive(cls, websocket):
|
|
126
|
+
"""Check if a websocket connection is still alive"""
|
|
127
|
+
try:
|
|
128
|
+
# Try to ping the websocket with a short timeout
|
|
129
|
+
await asyncio.wait_for(websocket.ping(), timeout=2.0)
|
|
130
|
+
return True
|
|
131
|
+
except (asyncio.TimeoutError, ConnectionError, Exception):
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
@classmethod
|
|
135
|
+
def __cleanup_dead_sessions(cls):
|
|
136
|
+
"""Remove dead sessions from tracking"""
|
|
137
|
+
current_time = time.time()
|
|
138
|
+
dead_sessions = []
|
|
139
|
+
|
|
140
|
+
for session_id, session_info in cls._active_sessions.items():
|
|
141
|
+
# Remove sessions older than 5 minutes (stale cleanup)
|
|
142
|
+
if current_time - session_info['timestamp'] > 300:
|
|
143
|
+
dead_sessions.append(session_id)
|
|
144
|
+
|
|
145
|
+
for session_id in dead_sessions:
|
|
146
|
+
del cls._active_sessions[session_id]
|
|
147
|
+
|
|
148
|
+
async def __handle_session_conflict(self, websocket, existing_session_info, new_session_id):
|
|
149
|
+
"""Handle session conflict by checking if existing connection is alive"""
|
|
150
|
+
existing_ws = existing_session_info['websocket']
|
|
151
|
+
existing_datapipe = existing_session_info['datapipe']
|
|
152
|
+
|
|
153
|
+
# Check if existing websocket is still alive
|
|
154
|
+
if await self.__is_websocket_alive(existing_ws):
|
|
155
|
+
# Existing connection is alive - send conflict message to BOTH connections
|
|
156
|
+
conflict_msg = {
|
|
157
|
+
'id': 'session_conflict',
|
|
158
|
+
'message': {
|
|
159
|
+
'type': 'session_conflict',
|
|
160
|
+
'error': 'Multiple windows/tabs detected. Please use only one browser window.',
|
|
161
|
+
'action': 'close_duplicate'
|
|
162
|
+
},
|
|
163
|
+
'direction': 'error'
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
# Send to existing connection
|
|
168
|
+
await existing_ws.send(serialize(conflict_msg))
|
|
169
|
+
|
|
170
|
+
# Send to new connection
|
|
171
|
+
await websocket.send(serialize(conflict_msg))
|
|
172
|
+
|
|
173
|
+
# Close the new connection
|
|
174
|
+
await websocket.close(code=1008, reason='Session conflict')
|
|
175
|
+
|
|
176
|
+
# Call abort on the existing DataPipe instance
|
|
177
|
+
if existing_datapipe.__abort is not None:
|
|
178
|
+
err = RuntimeError(f"Session conflict detected: New connection attempted with session {new_session_id}")
|
|
179
|
+
existing_datapipe.__abort(err)
|
|
180
|
+
|
|
181
|
+
return False # Reject new connection
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
print(f"Error handling session conflict: {e}")
|
|
185
|
+
# If we can't communicate, treat existing as dead
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
# Existing connection is dead - replace it
|
|
189
|
+
return True # Allow new connection
|
|
190
|
+
|
|
118
191
|
def register( self, ident, callback ):
|
|
119
192
|
"""Register a callback to handle all requests coming from JavaScript. The
|
|
120
193
|
callback will be called whenever a request arrives.
|
|
@@ -175,6 +248,8 @@ class DataPipe(DataSource):
|
|
|
175
248
|
"""
|
|
176
249
|
try:
|
|
177
250
|
self.__websocket = websocket
|
|
251
|
+
session_established = False
|
|
252
|
+
|
|
178
253
|
async for message in websocket:
|
|
179
254
|
msg = deserialize(message)
|
|
180
255
|
if 'session' not in msg:
|
|
@@ -185,8 +260,53 @@ class DataPipe(DataSource):
|
|
|
185
260
|
else:
|
|
186
261
|
raise err
|
|
187
262
|
return
|
|
263
|
+
|
|
264
|
+
# Handle session initialization with conflict detection
|
|
265
|
+
if not session_established:
|
|
266
|
+
new_session_id = msg['session']
|
|
267
|
+
|
|
268
|
+
with self._session_lock:
|
|
269
|
+
# Clean up any stale sessions first
|
|
270
|
+
self.__cleanup_dead_sessions()
|
|
271
|
+
|
|
272
|
+
# Check if session already exists
|
|
273
|
+
if new_session_id in self._active_sessions:
|
|
274
|
+
existing_session_info = self._active_sessions[new_session_id]
|
|
275
|
+
|
|
276
|
+
# Handle the conflict
|
|
277
|
+
if not await self.__handle_session_conflict(websocket, existing_session_info, new_session_id):
|
|
278
|
+
return # Connection was rejected
|
|
279
|
+
|
|
280
|
+
# Register this session as active
|
|
281
|
+
self._active_sessions[new_session_id] = {
|
|
282
|
+
'websocket': websocket,
|
|
283
|
+
'timestamp': time.time(),
|
|
284
|
+
'datapipe': self
|
|
285
|
+
}
|
|
286
|
+
self.__session = new_session_id
|
|
287
|
+
session_established = True
|
|
288
|
+
|
|
289
|
+
# Existing session validation (your original logic)
|
|
188
290
|
elif self.__session != None and self.__session != msg['session']:
|
|
189
|
-
|
|
291
|
+
conflict_msg = {
|
|
292
|
+
'id': self.__session,
|
|
293
|
+
'message': {
|
|
294
|
+
'type': 'session_corruption',
|
|
295
|
+
'error': 'Session corruption detected',
|
|
296
|
+
'expected': self.__session,
|
|
297
|
+
'received': msg['session']
|
|
298
|
+
},
|
|
299
|
+
'direction': 'error'
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
await self.__websocket.send(serialize(conflict_msg))
|
|
303
|
+
await self.__websocket.close()
|
|
304
|
+
|
|
305
|
+
# Clean up from active sessions
|
|
306
|
+
with self._session_lock:
|
|
307
|
+
if self.__session in self._active_sessions:
|
|
308
|
+
del self._active_sessions[self.__session]
|
|
309
|
+
|
|
190
310
|
err = RuntimeError(f"session corruption: {msg['session']} does not equal {self.__session}")
|
|
191
311
|
if self.__abort is not None:
|
|
192
312
|
self.__abort( err )
|
|
@@ -196,10 +316,11 @@ class DataPipe(DataSource):
|
|
|
196
316
|
|
|
197
317
|
with self.__lock:
|
|
198
318
|
###
|
|
199
|
-
### initialize session identifier
|
|
319
|
+
### initialize session identifier (pre-state-corruption fixes)
|
|
200
320
|
###
|
|
201
321
|
if self.__session == None:
|
|
202
322
|
self.__session = msg['session']
|
|
323
|
+
|
|
203
324
|
if msg['direction'] == 'p2j':
|
|
204
325
|
cb = self.__get_pending(msg['id'])
|
|
205
326
|
outgo = self.__dequeue_send(msg['id'])
|
|
@@ -255,4 +376,9 @@ class DataPipe(DataSource):
|
|
|
255
376
|
'exception': repr(e) },
|
|
256
377
|
'direction': str(msg['direction']) } ) )
|
|
257
378
|
finally:
|
|
379
|
+
# Clean up when connection closes
|
|
380
|
+
if self.__session is not None:
|
|
381
|
+
with self._session_lock:
|
|
382
|
+
if self.__session in self._active_sessions:
|
|
383
|
+
del self._active_sessions[self.__session]
|
|
258
384
|
self.__websocket = None
|
cubevis/toolbox/_cube.py
CHANGED
|
@@ -203,15 +203,19 @@ class CubeMask:
|
|
|
203
203
|
cube=casaimage.as_mime(join( dirname(dirname(__file__)), "__icons__", 'add-cube.png' ) ) )
|
|
204
204
|
_sub_ = dict( chan=casaimage.as_mime(join( dirname(dirname(__file__)), "__icons__", 'sub-chan.png' ) ),
|
|
205
205
|
cube=casaimage.as_mime(join( dirname(dirname(__file__)), "__icons__", 'sub-cube.png' ) ) )
|
|
206
|
+
_del_ = dict( chan=casaimage.as_mime(join( dirname(dirname(__file__)), "__icons__", 'trash.png' ) ),
|
|
207
|
+
cube=casaimage.as_mime(join( dirname(dirname(__file__)), "__icons__", 'trash_full.png' ) ) )
|
|
206
208
|
self._mask_icons_ = dict( on=casaimage.as_mime(join( dirname(dirname(__file__)), "__icons__", 'new-layer-sm-selected.png' ) ),
|
|
207
209
|
off=casaimage.as_mime(join( dirname(dirname(__file__)), "__icons__", 'new-layer-sm.png' ) ) )
|
|
208
210
|
self._mask_add_sub = { 'add': CustomAction( icon=_add_['chan'], name="Mask Add",
|
|
209
211
|
description="add region to current channel's mask (hold Shift key then click to add to all channels)" ),
|
|
210
212
|
'sub': CustomAction( icon=_sub_['chan'], name="Mask Sub",
|
|
211
213
|
description="subtract region from current channel's mask (hold Shift key then click to subtract from all channels)" ),
|
|
214
|
+
'dele': CustomAction( icon=_del_['chan'], name="Mask Delete",
|
|
215
|
+
description="delete current channel's mask (hold Shift key then click to delete the mask from all channels)" ),
|
|
212
216
|
'mask': CustomAction( icon=self._mask_icons_['off'], name="Mask Select",
|
|
213
217
|
description="select the mask for the current channel" ),
|
|
214
|
-
'img': dict( add=_add_, sub=_sub_ ) }
|
|
218
|
+
'img': dict( add=_add_, sub=_sub_, dele=_del_ ) }
|
|
215
219
|
|
|
216
220
|
self._fig = { }
|
|
217
221
|
self._hover = { 'spectrum': None, 'image': None } # HoverTools which are used to synchronize image/spectrum
|
|
@@ -438,6 +442,23 @@ class CubeMask:
|
|
|
438
442
|
err = "internal error: bad add/subtract scope"
|
|
439
443
|
else:
|
|
440
444
|
err = "internal error: bad add/subtract message"
|
|
445
|
+
elif msg['action'] == 'delete':
|
|
446
|
+
if msg['scope'] == 'chan':
|
|
447
|
+
mask = self._pipe['image'].mask( msg['value']['chan'], True )
|
|
448
|
+
mask.fill(0.0)
|
|
449
|
+
self._pipe['image'].put_mask( msg['value']['chan'], mask )
|
|
450
|
+
self._mask_id = str(uuid4( )) ### new mask identifier
|
|
451
|
+
return dict( result='success', update={ } )
|
|
452
|
+
elif msg['scope'] == 'cube':
|
|
453
|
+
stokes = msg['value']['chan'][0]
|
|
454
|
+
for c in range(shape[3]):
|
|
455
|
+
mask = self._pipe['image'].mask( [stokes,c], True )
|
|
456
|
+
mask.fill(0.0)
|
|
457
|
+
self._pipe['image'].put_mask( [stokes,c], mask )
|
|
458
|
+
self._mask_id = str(uuid4( )) ### new mask identifier
|
|
459
|
+
return dict( result='success', update={ } )
|
|
460
|
+
else:
|
|
461
|
+
err = "internal error: bad invert scope"
|
|
441
462
|
elif msg['action'] == 'not':
|
|
442
463
|
notf = np.vectorize(lambda x: 0.0 if x != 0 else 1.0)
|
|
443
464
|
if msg['scope'] == 'chan':
|
|
@@ -496,7 +517,8 @@ class CubeMask:
|
|
|
496
517
|
ResetTool(name="Reset"), PolySelectTool(name="Poly Mask") ] +
|
|
497
518
|
( [ self._mask_add_sub['add'],
|
|
498
519
|
self._mask_add_sub['sub'],
|
|
499
|
-
self._mask_add_sub['mask']
|
|
520
|
+
self._mask_add_sub['mask'],
|
|
521
|
+
self._mask_add_sub['dele'] ] if self._mask_path else [ ] ),
|
|
500
522
|
tooltips=None ), **kw )
|
|
501
523
|
|
|
502
524
|
###
|
|
@@ -1859,6 +1881,21 @@ class CubeMask:
|
|
|
1859
1881
|
self._js_mode_code['bitmask-hotkey-setup-add-sub'] +
|
|
1860
1882
|
'''if ( cb_obj._mode == 'cube' ) mask_sub_cube( )
|
|
1861
1883
|
else mask_sub_chan( )''' )
|
|
1884
|
+
self._mask_add_sub['dele'].callback = CustomJS( args=dict( annotations=self._annotations,
|
|
1885
|
+
source=self._image_source,
|
|
1886
|
+
ctrl=self._pipe['control'],
|
|
1887
|
+
ids=self._ids,
|
|
1888
|
+
stats_source=self._statistics_source,
|
|
1889
|
+
mask_region_icons=self._mask_icons_,
|
|
1890
|
+
mask_region_button=self._mask_add_sub['mask'],
|
|
1891
|
+
mask_region_ds=self._bitmask_contour_maskmod_ds,
|
|
1892
|
+
contour_ds=self._bitmask_contour_ds,
|
|
1893
|
+
disable_add_sub = self._mask_add_sub_disable,
|
|
1894
|
+
status=self._status_div ),
|
|
1895
|
+
code=self._js['update-status'] +
|
|
1896
|
+
self._js_mode_code['bitmask-hotkey-setup-add-sub'] +
|
|
1897
|
+
'''if ( cb_obj._mode == 'cube' ) mask_del_cube( )
|
|
1898
|
+
else mask_del_chan( )''' )
|
|
1862
1899
|
|
|
1863
1900
|
self._mask_add_sub['mask'].callback = CustomJS( args=dict( annotations=self._annotations,
|
|
1864
1901
|
contour_ds=self._bitmask_contour_ds,
|
|
@@ -2329,6 +2366,19 @@ class CubeMask:
|
|
|
2329
2366
|
mask_mod_result )
|
|
2330
2367
|
} else if ( status ) status.text = '<p>no region found</p>'
|
|
2331
2368
|
}
|
|
2369
|
+
function mask_del_chan( ) {
|
|
2370
|
+
if ( disable_add_sub.values.disabled ) {
|
|
2371
|
+
update_status_error( disable_add_sub.values.message ?
|
|
2372
|
+
disable_add_sub.values.message :
|
|
2373
|
+
default_add_sub_disabled_text )
|
|
2374
|
+
return
|
|
2375
|
+
}
|
|
2376
|
+
ctrl.send( ids['mask-mod'],
|
|
2377
|
+
{ scope: 'chan',
|
|
2378
|
+
action: 'delete',
|
|
2379
|
+
value: { chan: source.cur_chan } },
|
|
2380
|
+
mask_mod_result )
|
|
2381
|
+
}
|
|
2332
2382
|
function mask_add_cube( ) {
|
|
2333
2383
|
if ( disable_add_sub.values.disabled ) {
|
|
2334
2384
|
update_status_error( disable_add_sub.values.message ?
|
|
@@ -2376,6 +2426,19 @@ class CubeMask:
|
|
|
2376
2426
|
src: mask_region_ds._src_chan } },
|
|
2377
2427
|
mask_mod_result )
|
|
2378
2428
|
} else if ( status ) status.text = '<p>no region found</p>'
|
|
2429
|
+
}
|
|
2430
|
+
function mask_del_cube( ) {
|
|
2431
|
+
if ( disable_add_sub.values.disabled ) {
|
|
2432
|
+
update_status_error( disable_add_sub.values.message ?
|
|
2433
|
+
disable_add_sub.values.message :
|
|
2434
|
+
default_add_sub_disabled_text )
|
|
2435
|
+
return
|
|
2436
|
+
}
|
|
2437
|
+
ctrl.send( ids['mask-mod'],
|
|
2438
|
+
{ scope: 'cube',
|
|
2439
|
+
action: 'delete',
|
|
2440
|
+
value: { chan: source.cur_chan } },
|
|
2441
|
+
mask_mod_result )
|
|
2379
2442
|
}''',
|
|
2380
2443
|
'bitmask-hotkey-setup': '''
|
|
2381
2444
|
function state_translate_selection( dx, dy ) {
|
|
@@ -2875,18 +2938,23 @@ class CubeMask:
|
|
|
2875
2938
|
}''',
|
|
2876
2939
|
'cube-init': '''add._mode = 'chan'
|
|
2877
2940
|
sub._mode = 'chan'
|
|
2941
|
+
dele._mode = 'chan'
|
|
2878
2942
|
let foo = casalib.is_empty
|
|
2879
2943
|
function cube_on( ) {
|
|
2880
2944
|
add._mode = 'cube'
|
|
2881
2945
|
add.icon = img['add']['cube']
|
|
2882
2946
|
sub._mode = 'cube'
|
|
2883
2947
|
sub.icon = img['sub']['cube']
|
|
2948
|
+
dele._mode = 'cube'
|
|
2949
|
+
dele.icon = img['dele']['cube']
|
|
2884
2950
|
}
|
|
2885
2951
|
function cube_off( ) {
|
|
2886
2952
|
add._mode = 'chan'
|
|
2887
2953
|
add.icon = img['add']['chan']
|
|
2888
2954
|
sub._mode = 'chan'
|
|
2889
2955
|
sub.icon = img['sub']['chan']
|
|
2956
|
+
dele._mode = 'chan'
|
|
2957
|
+
dele.icon = img['dele']['chan']
|
|
2890
2958
|
}
|
|
2891
2959
|
casalib.hotkeys( '*',
|
|
2892
2960
|
{ keyup: true, scope: 'all' },
|
|
@@ -22,6 +22,11 @@ cubevis/__icons__/sub-chan.png,sha256=wQ-qasFXUIOTf2u1FYDlLw1sOGD9Lc8DxQlazxdvdw
|
|
|
22
22
|
cubevis/__icons__/sub-chan.svg,sha256=hEmxtj0ogChOCPPDud-Nki317jxa0mGk-GAjWqVOyj4,2563
|
|
23
23
|
cubevis/__icons__/sub-cube.png,sha256=mtv21frhc_GuXsfDlUa-0xQzym3Ii4TZiQGm4MFYjrY,10011
|
|
24
24
|
cubevis/__icons__/sub-cube.svg,sha256=tDCFZ2Es6b8HTiBpgLPG0cOOnlS8dI7LbE2KQxEWFw4,6391
|
|
25
|
+
cubevis/__icons__/trash.png,sha256=Y0Y5OWvhKMvYFOpnlTxwr0jve4eDgUmVZutOROQvtSY,25995
|
|
26
|
+
cubevis/__icons__/trash.svg,sha256=CVF_NUK_VY4ZDoExRY7o10PrzOGSL9av6xajhCgVD9E,979
|
|
27
|
+
cubevis/__icons__/trash_full.png,sha256=1rDPnltwC2N4TEmxDxgh2-KahIJY99wjb6JOBLmDyK8,20710
|
|
28
|
+
cubevis/__icons__/trash_full.svg,sha256=DAvMdNIDPDcSG3LTYEamonaYJJWGp07S2mAMK7p62Qg,1946
|
|
29
|
+
cubevis/__icons__/trash_full_raw.png,sha256=8ChZlNOqnDqmNhNYoqyLiGluYwJSYeQ2UvNLqVUX5hI,68993
|
|
25
30
|
cubevis/__icons__/zoom-to-fit.png,sha256=aozGbrkoBX1q9kV0p272bg0YKGje1gIfVBkFZRlLAXs,9592
|
|
26
31
|
cubevis/__icons__/zoom-to-fit.svg,sha256=NtYorWvH4s68iAMriqCPGuTBX5SsgVN310UXGKDM7i8,1802
|
|
27
32
|
cubevis/__init__.py,sha256=63Y0ROAWuHV1ZHJ8JX7NZl9GQJOaNZI3P1RAhIQkdd0,3296
|
|
@@ -29,7 +34,7 @@ cubevis/__js__/bokeh-3.6.1.min.js,sha256=SPRs94Q-H-aj8MCsXNu4ok1ouQQLTgXxZnk0-BB
|
|
|
29
34
|
cubevis/__js__/bokeh-tables-3.6.1.min.js,sha256=wINufoBiINmP_PERwhN_1GkidJOsJQ_3vFKUDui7rl8,301216
|
|
30
35
|
cubevis/__js__/bokeh-widgets-3.6.1.min.js,sha256=NE3tFbbxoaMjnJ0XednWJxbAGl-vSR0fxE_kX8keuDQ,311821
|
|
31
36
|
cubevis/__js__/casalib.min.js,sha256=JLZ_3i5JlbNJw2nsx7pewysxzoD3sVpSiWdgJCLbhi0,91107
|
|
32
|
-
cubevis/__js__/cubevisjs.min.js,sha256=
|
|
37
|
+
cubevis/__js__/cubevisjs.min.js,sha256=rs84yRDDnHJ4dq1DM9eVTCqRIo9LedSp8pZVFAX9Tzs,31088
|
|
33
38
|
cubevis/bokeh/__init__.py,sha256=XvuKcU9-bAv1CPb_O81VJTNLlHQC-zBg_Ig9_q4RkM4,1371
|
|
34
39
|
cubevis/bokeh/annotations/__init__.py,sha256=tjDIPKbg-rh7Iu3coFWvmX-j2yNj9KuKmRp1aTo71ww,50
|
|
35
40
|
cubevis/bokeh/annotations/_ev_poly_annotation.py,sha256=0ayX21gxNnm5-4s5VRKiaJ23DCSvbvpsU6oym9q2bk0,173
|
|
@@ -44,7 +49,7 @@ cubevis/bokeh/models/_shared_dict.py,sha256=AWjLMOKVAXWHSF4gcK2RbxF442QlzQ7hMR0o
|
|
|
44
49
|
cubevis/bokeh/models/_tip.py,sha256=bnlCOYXsNdgEYKDbsQ6hEVrKOjCXEDxtuwE3sldOeEU,1396
|
|
45
50
|
cubevis/bokeh/models/_tip_button.py,sha256=Dw4aO37o0J3n6EoaJ3ui1y8d04qNn3x2dbKiwiQpArM,1577
|
|
46
51
|
cubevis/bokeh/sources/__init__.py,sha256=4FsudFuVU4o7VG5OG3K1tiMoxIXcJWNz_K9yzMDE8ls,1581
|
|
47
|
-
cubevis/bokeh/sources/_data_pipe.py,sha256=
|
|
52
|
+
cubevis/bokeh/sources/_data_pipe.py,sha256=woG2_r4p1GxlclRQ6kCQEhWp7jMaHUJHkgjP_BbUx7U,19112
|
|
48
53
|
cubevis/bokeh/sources/_image_data_source.py,sha256=ezgTWHtHMpbT-jAd1F_xnq3jdcvIGbqNvN9MTbTOnNs,3537
|
|
49
54
|
cubevis/bokeh/sources/_image_pipe.py,sha256=4pvoy517N199OMD_q40NkPChsGPSMaR58hw1Gb348NU,28475
|
|
50
55
|
cubevis/bokeh/sources/_spectra_data_source.py,sha256=Tbq4JczbmWfW9OPTzY2OuCMPOrr9yGQ1xVB63QTHnOw,2200
|
|
@@ -96,7 +101,7 @@ cubevis/remote/_local.py,sha256=PcPCFcwttTFZd3O33-5pqDuGKQKK6CA0gz1MTIkTiNI,1032
|
|
|
96
101
|
cubevis/remote/_remote_kernel.py,sha256=wfu7ZzKn-oCxZxzDIkC5puBvGf8WbCLYL3CzM56_FNc,2652
|
|
97
102
|
cubevis/toolbox/__init__.py,sha256=xzqwAG9863d7UKBVBRw7FrRUQbvCdFcLBq4vTpg63DU,1487
|
|
98
103
|
cubevis/toolbox/_app_context.py,sha256=0tRY2SSbSCM6RKLFs_T707_ehWkJXPvnLlE1P9cLXJY,3024
|
|
99
|
-
cubevis/toolbox/_cube.py,sha256=
|
|
104
|
+
cubevis/toolbox/_cube.py,sha256=iFIaqYvyIfRJg_z9IUJxUGhkyeZc3InF1axzra1YHw0,305724
|
|
100
105
|
cubevis/toolbox/_interactive_clean_ui.mustache,sha256=1vGWbm0_1vkd9cJSiAwjAGYCXbWxAmi_Ll9Dwo8wKxI,112022
|
|
101
106
|
cubevis/toolbox/_interactive_clean_ui.py,sha256=upKXlTUIuToZJEZPy3qBUfppgCfoLchEMqyByXQsQWA,111933
|
|
102
107
|
cubevis/toolbox/_interactiveclean_wrappers.py,sha256=XqyCGz33CMDhszTxnwZ_3-64GszUK1XYnGKUOxl9sas,5071
|
|
@@ -115,8 +120,8 @@ cubevis/utils/_pkgs.py,sha256=mu2CCzndmJZYP81UkFhxveW_CisWLUvagJVolHOEVgM,2294
|
|
|
115
120
|
cubevis/utils/_regions.py,sha256=TdAg4ZUUyhg3nFmX9_KLboqmc0LkyOdEW8M1WDR5Udk,1669
|
|
116
121
|
cubevis/utils/_static.py,sha256=rN-sqXNqQ5R2M3wmPHU1GPP5OTyyWQlUPRuimCrht-g,2347
|
|
117
122
|
cubevis/utils/_tiles.py,sha256=A9W1X61VOhBMTOKXVajzOIoiV2FBdO5N2SFB9SUpDOo,7336
|
|
118
|
-
cubevis/__version__.py,sha256=
|
|
119
|
-
cubevis-0.5.
|
|
120
|
-
cubevis-0.5.
|
|
121
|
-
cubevis-0.5.
|
|
122
|
-
cubevis-0.5.
|
|
123
|
+
cubevis/__version__.py,sha256=pRGq-OZp7rhvAQs6LmIPCzIsSAQhCXt-lXUWw_8ZaSw,22
|
|
124
|
+
cubevis-0.5.29.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
|
|
125
|
+
cubevis-0.5.29.dist-info/METADATA,sha256=SxfkpXe33VEnFz45bdusC35zgQoi82P3ot46p9q6wp4,2629
|
|
126
|
+
cubevis-0.5.29.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
|
|
127
|
+
cubevis-0.5.29.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|