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.

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
@@ -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("b33e822163");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 T=a("cb7d28d6b3");c("DragTool",T.DragTool);const d=a("01959f25a3");c("CBResetTool",d.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:T.DragTool,CBResetTool:d.CBResetTool,Tip:b.Tip,TipButton:u.TipButton,SharedDict:g.SharedDict,EditSpan:f.EditSpan,EvTextInput:E.EvTextInput,EvPolyAnnotation:I.EvPolyAnnotation})},
46
- "b33e822163": function _(e,s,t,i,n){var o;i();const c=e("@bokehjs/models/sources/data_source"),a=e("e3901fa9f2"),d=e("@bokehjs/core/util/callbacks");class l extends c.DataSource{constructor(e){super(e),this.send_queue={},this.connection_queue=[],this.pending={},this.incoming_callbacks={}}initialize(){super.initialize();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,a.deserialize)(e.data);if("id"in s&&"direction"in s&&"message"in s){let{id:e,message:t,direction:i}=s;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,a.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,a.serialize)({id:e,direction:i,message:s,session:casalib.object_id(this)}))}}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,a.serialize)({id:"initialize",direction:"j2p",session:casalib.object_id(this)})),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)}}};t();(()=>{null!=this.init_script&&(0,d.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:casalib.object_id(this)};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 c of this.send_queue[e])i(c.msg.message)&&(c.msg=n,c.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:d,msg:l}=this.send_queue[e].shift();if(this.pending[e]={cb:d},this.websocket.readyState===WebSocket.OPEN)this.websocket.send((0,a.serialize)(l));else{let u=20,h=this;function r(){h.websocket.readyState===WebSocket.OPEN?h.websocket.send((0,a.serialize)(l)):(u-=1,u>0&&setTimeout(r,3e3))}setTimeout(r,3e3)}}}else if(this.websocket.readyState===WebSocket.OPEN)this.pending[e]={cb:t},this.websocket.send((0,a.serialize)(n));else{let b=20,g=this;function _(){g.websocket.readyState===WebSocket.OPEN?(g.pending[e]={cb:t},g.websocket.send((0,a.serialize)(n))):(b-=1,b>0&&setTimeout(_,3e3))}setTimeout(_,3e3)}}}t.DataPipe=l,o=l,l.__name__="DataPipe",l.__module__="cubevis.bokeh.sources._data_pipe",o.define((({Any:e,Tuple:s,String:t,Number:i})=>({init_script:[e,null],address:[s(t,i)]})))},
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("b33e822163");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]})))},
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("b33e822163"),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]})))},
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":"b33e822163","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"}, {});});
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.27'
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
- await self.__websocket.close( )
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'] ] if self._mask_path else [ ] ),
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' },
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cubevis
3
- Version: 0.5.27
3
+ Version: 0.5.29
4
4
  Summary: visualization toolkit and apps for casa
5
5
  License: LGPL
6
6
  Author-email: Darrell Schiebel <darrell@schiebel.us>,Pam Harris <pharris@nrao.edu>
@@ -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=60KOq5Nt2OW_eGuJ_BcI-GndTEQAL4gZNkbZE9Lve0M,28238
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=PI4vZ_4eywHJfGEanyx-eMJvoeuMrPdMcF_ZbScYSi8,13779
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=k9y2KnGXmZa8LUTx7e6YVMSIBTN7D_8sE9Jv7oRMqf4,299958
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=D3KnqWLhEOflsxhBRg-IJobieoAgxcLEYGOcdU-Jfk0,22
119
- cubevis-0.5.27.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
120
- cubevis-0.5.27.dist-info/METADATA,sha256=LF-IkQ0bRLXzM6tcvSVJUELcu88dlK-DHOhLZ9hcYgo,2629
121
- cubevis-0.5.27.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
122
- cubevis-0.5.27.dist-info/RECORD,,
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,,