package require Buplot canvaspathName create bptracepanel viewportName ?options?
Trace data being accumulated with this canvas item are pre-pixelated into buffers that correspond to the horizontal (time) pixel range being displayed. Certain display functions can be quickly executed from these pixelated buffers as long as the time scaling has not changed. These functions include trace amplitude scaling, trace plot mode changes (see adisplay_mode below) and changing of trace vertical positions and heights.
A bptracepanel item is created with the following command:
With the following item options:
The following item options are specific to btracepanel items:
instance 0 trhndl.000 &Arr{ label test00 filter none db NULL orb NULL nscl NULL visible 1 y -1 h -1 y_trplot 0 h_trplot 99 background_color NULL background 4294967295 tags &Tbl{ } }
The instance parameter is a copy of the instance counter specified when the callback was defined in the item configuration call. There will always be a single associate array parameter with the name set to the trace string handle assigned by the bptracepanel canvas item object. Each new trace that is created is assigned a unique trace string handle by the bptracepanel canvas item object code and this trace string handle is used by all subsequent calls as a way of specifying particular traces for action. For instance, when a trace is meant to display data from a buvector(3) object, as specified through the -data option described above, the application script code would typically call the -data item configuration option within this callback. The application script will not know the trace string handle until this callback is executed and that handle is necessary in the -data item configuration call. For ORB and Datascope data, the data plumbing happens automatically and typically does not require the connection to be in this callback.
For this particular example the trace string handle is trhndl.000 and the associative array contains parameter data for this trace including the trace label, any defined trace tags, the defined orbtag (orb) or dbtag (db) along with the nscl and sc values, the background color and the position (y) and height (h). The actual computed position and height where the trace will be rendered are specified as y_trplot and h_trplot.
For ORB data sources, the Antelope parameter file string also contains a channel parameter which is in the form orbtag:nscl which specifies the new data source and SEED net_sta_chan[_loc]. Following is an example of the returned parameter file string for a new channel.
instance 0 channel ucsdorb:N4_P46A_HHE
In this example a new ORB channel with seed code N4_P46A_HHE has been read from the ORB with orbtag ucsdorb. New channels are only processed with this callback once. This callback provides a means for determining the traces to display and their positions at run time in reponse to the data streams instead of having to specify them as a static list before any data connections are processed. For instance, in the orbrtd(1) script ORB data channels for display can be specified using regular expression matches against the real-time data streams instead of having to pre-assign trace displays statically at startup. This is accomplished using this callback. If a new channel should be displayed, then a bptracepanel item configuration call should be made in this callback using the -trace option described above.
For Datascope database data sources, the Antelope parameter file string also contains a dbchannels parameter which is a list of strings with each string reresenting a single channel from the database in the form sta chan nscl tstart tend, where sta chan are the CSS station and channels codes, nscl is the corresponding SEED net_sta_chan[_loc], tstart is the earliest epoch time of the channel and tend is the latest epoch time of the channel. Following is an example of the returned parameter file string for a new channel. As well, the parameter file contains a dbtags parameter with the database tag name.
instance 0 dbtag datadb dbchannels &Tbl{ AAK BHE ZZ_AAK_BHE 706139710.250000 706139822.650000 AAK BHN ZZ_AAK_BHN 706139710.250000 706139822.650000 AAK BHZ ZZ_AAK_BHZ 706139710.250000 706139822.650000 CHM BHE ZZ_CHM_BHE 706139715.200000 706139841.500000 CHM BHN ZZ_CHM_BHN 706139715.200000 706139841.500000 CHM BHZ ZZ_CHM_BHZ 706139715.200000 706139841.500000 EKS2 BHE ZZ_EKS2_BHE 706139704.700000 706139801.550000 EKS2 BHN ZZ_EKS2_BHN 706139704.700000 706139801.550000 EKS2 BHZ ZZ_EKS2_BHZ 706139704.700000 706139801.550000 KBK BHE ZZ_KBK_BHE 706139714.400000 706139838.450000 KBK BHN ZZ_KBK_BHN 706139714.400000 706139838.450000 KBK BHZ ZZ_KBK_BHZ 706139714.400000 706139838.450000 KMI BHN ZZ_KMI_BHN 706139638.905640 706139638.905640 TKM BHE ZZ_TKM_BHE 706139719.050000 706139855.950000 TKM BHN ZZ_TKM_BHN 706139719.050000 706139855.950000 TKM BHZ ZZ_TKM_BHZ 706139719.050000 706139855.950000 USP BHE ZZ_USP_BHE 706139715.700000 706139843.350000 USP BHN ZZ_USP_BHN 706139715.700000 706139843.350000 USP BHZ ZZ_USP_BHZ 706139715.700000 706139843.350000 }
For database sources this callback is only made once when the database is opened. All of the channels and time ranges are specified in the single call.
layer_definitions &Arr{ data_bkg &Arr{ plot data type bkg color #000080 } data_trace &Arr{ plot data type trace color &Tbl{ yellow #ff8000 #ff4000 #ff0000 } scale_mode auto scale_range full again 0.8 } } layers &Tbl{ data_bkg data_trace }
The parameter file must contain an associate array named layer_definitions and a table named layers. The layer_definitions array defines the various plotting layers that will be displayed. Each entry in the layer_definitions array is another associative array with a unique and arbitrary layer name. Within each layer are other parameters that define the appearance of that layer. The layers table defines the layers to be displayed and the order in which they are to be rendered. Layer plotting parameters are defined in the following section LAYER RENDERING.
trace_handle trhndl.009 data_trace &Arr{ again 1.5 }
The parameter file must contain a parameter named trace_handle which must be set to the trace string handle of the trace whose parameters are to be modified. There must also be one or more associate arrays with names corresponding to the default names as defined in the default layer_definitions array set by the -plot_params option. Within each of the layers are the parameters that are to be changed. For this example the again parameyer for the data_trace layer is changed to 1.5 for the trace with string handle trhndl.009.
However, trace plotting has some distinct differences from the typical bppolyline function plotting. First, trace waveform data can contain many more points than a typical generic X-Y function that would be plotted by bppolyline. Also, it is often desirable to display large numbers of traces in record sections as well as long time durations in which a single time pixel might contain a large number of trace samples. For example, if we wanted to plot 100 traces over a time window of one week in which the trace channels were sampled at 100 samples per second, the input trace sample data would take up at least 24 Gbytes of data. We could not cache that much data without incuring severe performance penalties. If we were to just represent the 100 traces over one week as buvector(3) objects and try to plot them with bppolyline, we could run out of heap memory altogether or at the very least incur severe paging performance degredation which would cause the display to be very slow. The display of large amounts of dense trace data requires a different approach. Instead of reading all the data into a heap cache, we need to only read the data when it is time to plot the data. That is the way that the dbpick(1) program works. The only data read is the data being displayed and the data is only read when the display is rendered.
Second, trace waveform data can come in an incremental real-time manner, such as trace data being acquired using an Antelope ORB. In this situation it is desirable to make animated time scrolling displays, such as the displays made by orbmonrtd(1). Data and displays of this type cannot be assimilated a priori into heap memory caches but must be assimilated in pieces as the data is read. A related issue with real-time trace data is that it is desirable to automatically configure the entire trace display dynamically as new data channels become available.
These important differences between waveform trace data and generic X-Y function data make it difficult to maintain a simple linear interface to the application code. Within the application code we often do not know beforehand how to even configure the display since we may not be sure what data channels will be in the database, which can change with time, or ORB, which definitely changes with time, where we are getting the data. We deal with these issues in bptracepanel through a set of configuration options in which we describe the data "plumbing" between traces and data sources. The actual reading and transfer of the data from the source into the display is then done automatically as the data needs to be displayed without any intervention at the scripting level.
The direct way of connecting data sources to display traces is through the -orb, -db and -trace item configfuration options described previously. The idea is to set up continuous data sources from either ORBs or Datascope databases through the -orb and -db item configuration options. Specifying these options causes separate data reading and prcessing threads to be launched for each data source. Once the data sources have been defined and launched, individual display traces can be specified through the -trace item configuration option. This option basically defines where a single trace will be displayed within the bptracepanel item window and where that trace will get its data. All of the memory management, data processing and display rendering associated with tha data trace are then handled automatically by the bptracepanel item. Each ORB or database data source is identified through tag strings assigned by the application programmer. Further identification of data channels is through specification of SEED net_sta_chan_loc codes, for ORB data sources, or CSS "sta chan" codes, for database data sources.
After setting up data sources and plumbing the data channels to the various trace displays using the appropriate bptracepanel item configuration options, the actual internal processing and display rendering is deferred until data is available and ready for displaying. With this approach you can configure your traces through the -trace option before you actually start up your data sources through the -orb and -db options. Once the data sources are started, all of the internal configuration is data driven.
An important feature of bptracepanel is that it will support dynamic trace configuration based upon the data streams themselves. An example of this can be seen in orbrtd(1) where regular expressions can be used to define the channels to be displayed. This is accomplished through the -new_channel_callback bptracepanel item configuration option. bptracepanel items use application programmer defined python callback procedures to effect data driven dynamic configuration. By registering a python callback with the -new_channel_callback option, this callback will be called whenever a new channel of data is seen by any of the data streams. The callback can then look at the data source tag and channel code to determine if the data should be displayed. The callback procedure effects the display of new channels by making one or more calls to the -trace item configuration option to add in new channels (note that a single channel of input data can be displayed across multiple bptracepanel trace displays).
Another important callback is specified through the -trace_creation_callback bptracepanel item configuration option. This is also a python callback that is called whenever a new trace has been created and is ready for rendering. The callback is passed an argument that contains all of the bptracepanel internal parameters associated with that trace. In particular, an important internal parameter called the trace handle string is passed back to the application script through this callback. Internally, bptracepanel keeps track of the individual traces through a unique string assigned by bptarcepanel to each trace known as the trace handle string. Subsequent calls to bptracepanel item configuration options that modify various parameters associated with a particular trace must contain this trace handle string as a reference to the trace. The application script is made aware of the trace handle string for newly created traces through the trace creation callback procedure. Note that application defined tags for each of the traces can be assigned through the -trace item configuration option with the tag= fields and these fields will show up in the trace creation callback arguments. This provides the application programmer with a way to identify new traces and associate the bptracepanel assigned trace handle strings with application program specific configuration parameters.
As described previously the dense nature of waveform data causes poor performance when using straightforward rendering methods. In bptracepanel items the incoming waveform data is always first rendered into what we call a pixelation buffer. The idea behind a pixelation buffer is that it maps data sample values into time pixel slots based on the currently defined buffer pixel width and time values at each edge of the buffer. Since many data samples may end up landing in the sampe time pixel slot, the internal pixelation buffer contains only the minimum and maximum data samples in each of the time pixel slots. Given a pixelation buffer width of 1000 pixels, the amount of heap memory required for a single trace would be about 8000 bytes regardless of the sample rate of the data being displayed. That turns our example described previously into a heap memory buffer of about 800,000 bytes instead of the 24 Gbytes of raw data. Much more manageable. A second rendering stage then takes place in which the traces are rendered into an actual pixmap from the internal pixelation buffer. In this stage only the minimum and maximum trace values over the 1000 time pixels are rendered instead of the original 60 million sample values from each original trace and the only plot scaling and pixelation is done in the vertical direction since the time scaling and pixelation has already be done. This means that the display rendering can quickly adapt to any changes that do not involve changing the time scaling, such as auto amplitude scaling or even changing display modes from normal line plotting to something more exotic, such as various color plotting methods.
Even in situations where the time values change at each edge of the internal pixelation buffer, as long as the time window or the buffer width do not change it is often possible to simply shift the buffer in situations where the time at both ends changes equally, such as a time shift that would come about in an animated real-time display. After applying a time shift as an integer pixel shift, new data could then be accumulated into the existing pixelation buffer without the need to repixelate the entire buffer. This is the manner in which orbrtd(1) works when it is displaying data from real-time ORB acquisition. The required computer resources are minimized in this fashion.
However, there are situations in which the internal pixelation buffer time scales or width do change. When this happens it is necessary to completely rerender the pixelation buffer from the original data samples. Since the raw input data is not cached in heap memory, repixelation requires some application program intervention and this is accomplished through the -repixelate_callback bptracepanel item configuration option. This is also a python callback that is called whenever a bptracepanel item internal pixelation buffer needs to be rerendered. The strategy for replaying the data sources for rerendering is up to the application programmer. When a repixelation occurs the bptracepanel item will automatically clear out the existing pixelation buffer as well as clear out the display pixmap. A simple strategy for repixelation when the data source is from an ORB is to rewind that data source through a call to the -orb bptracepanel item configuration option using the after= field which will cause the ORB read pointer to be reset and the subsequent display rendering will happen naturally and automatically as the ORB data is reread and processed.
A side effect of this approach to dynamic data processing is that when trace data is assimilated within the application program directly, such as the generation of a test signal as a buvector(3) object, the manner in which the application generated trace data is made available to the bptracepanel item becomes manual instead of automatic for ORB and database sources. This can be seen in the code example that follows. Our code example generates a sawtooth test signal as a single buplot(3) object and then displays this same trace over 10 traces within a bptracepanel item. The display is set up to slave the bptracepanel item window to the viewport window. The initial specification of the 10 traces is accomplished through 10 calls to the -trace bptracepanel item configuration option with only the label= and positioning fields specified. Note there are no orb= or db= fields since there are no such data sources in the example. You can specify traces like this with no data sources. They will display with their trace labels as blank traces. The -trace_create_callback option is used to specify a python callback procedure when the traces are created. In the example trace creation callback procedure we can see that the data gets injected through the -data bptracepanel item configuration option. Calling this option causes the vector data to be immediately rendered into the bptracepanel item internal pixelation buffer. Because the example is set up to slave the bptracepanel item display window to be slaved to the viewport, the internal pixelation buffer must be rerendered whenever the window is resized horizontally. The rerendring of the pixelation buffer is accomplished through the -repixelate_callback option and the repixelation callback procedure in the example. All this procedure does is to reinject the vector data using the same -data configuration option as in the trace creation callback. This is the basic sequence of calls that should be made for any data that is generated and managed within the application programs.
#!/opt/antelope/python2.7.6/bin/python import os import sys import signal signal.signal(signal.SIGINT, signal.SIG_DFL) sys.path.append(os.environ['ANTELOPE'] + "/data/python") import antelope.elog as elog import antelope.stock as stock from antelope.buplot import * from antelope.buvector import * class PfSubs(object): """ PfSubs is a set of class methods for converting Antelope parameter file strings to/from python dictionaries """ def __init__ (self): return @classmethod def pfstring2py (self, pfstring, pyd=None): """ PfSubs.pfstring2py(pfstring, pyd=None) is a class method for converting Antelope parameter file strings to python dictionaries Parameters ---------- pfstring: input Antelope parameter file string pyd: optional input python dictionary object. If specified then the output phython disctionary object is formed by appending the Antelope parameter file string to the input object Returns ------- pyd: output python dictionary object """ pf = stock.ParameterFile() pf.pfcompile (pfstring) pyf = pf.pf2dict() if pyd != None: pyd.update(pyf) else: pyd = pyf return (pyd) @classmethod def py2pfstring (self, pyd): """ PfSubs.py2pfstring(pyd) is a class method for converting python dictionaries to Antelope parameter file strings Parameters ---------- pyd: input python dictionary object Returns ------- pfstring: output Antelope parameter file string """ pf = stock.ParameterFile() pf['foo'] = pyd pfstr = pf.pf2string() pfstr = pfstr[4:-2] return pfstr class TraceDisplay(object): """ TraceDisplay() is the main trace display class. In this example all of the parameters are hard-wired internally in the class code. """ instance = -1 instances = {} def __init__ (self): """ set up an object instance counter and an object instance dictionary """ TraceDisplay.instance += 1 TraceDisplay.instances[TraceDisplay.instance] = self """ grab buplot main window """ self.mw = Buplot().mw """ set root window title and geometry """ self.mw.wm_title("test_bptracepanel") self.mw.geometry ("1000x1000") self.mw.grid_rowconfigure ( 0 , weight = 1 ) self.mw.grid_columnconfigure ( 0 , weight = 1 ) """ set 80 pixel left margin where the channel labels willl be displayed """ self.mleft = 80 """ set times at right and left ends of window """ self.timeright = stock.now() self.twin = 600.0 self.timeleft = self.timeright - self.twin """ create stock tkinter canvas - note that dimensions are not set, they will slave to the root window """ self.canvas = BCanvas ( self.mw, borderwidth = 0, highlightthickness = 0, background = "#800000" ) """ and this is what slaves the canvas dimensions to the root window """ self.canvas.grid_configure ( column = 0, row = 0, sticky = 'nsew') """ create a buplot viewport as a canvas item and set the left margin, note that since the viewport dimensions were not specified, the will slave to the canvas dimensions """ self.vp_item = self.canvas.create_bpviewport ("tracevp", 0, 0, mleft = self.mleft, mright = 0, mbottom = 0, mtop = 0, fill = '#e0e0e0', fill_frame = 'lightblue', tag = "tracevp") """ create the buplot tracepanel as a canvas item, set the trace_creation_callback and repixelate_callback to TraceDisplay class methods (note the use of the class instance variable that will be used by the class method callback in instantiate the object making the callback), set the time scales, set the panel and pixmap plot scales and dimensions so they are slaved to the viewport (this will cause these to automatically adjust to changing window dimensions) """ self.tr_item = self.canvas.create_bptracepanel ("tracevp", trace_creation_callback = 'TraceDisplay.trace_creation_callback %d' % TraceDisplay.instance, repixelate_callback = 'TraceDisplay.repixelate_callback %d' % TraceDisplay.instance, timeright = self.timeright, twin = self.twin, panel_height = 'viewport', pixmap_height = 'viewport', panel_width = 'viewport', pixmap_width = 'viewport' ) """ setup a test data trace as a buvector2 object """ self.vec = self.test_data (self.timeleft, self.timeright) """ initialize an internal object list of traces and a dictionary for holding the data input specifications for each trace """ self.traces = [] self.dataspcs = {} """ create ten test traces, the arguments to the trace creation option (trace=) are the trace label (label=), the trace y position (y=) and the trace height (h=), note the trace y position is set to 'auto', meaning automatically compute the trace y position so it is adjacent to the prior trace, and the trace height is 'fit', meaning the trace heights are all adjusted to fit within the pixmap height, which is slaved to the viewport height, which is slaved to the canvas height, which is slaved to the root window height """ for i in range(10): args = "label=test%2.2d,y=auto,h=fit" % i self.canvas.itemconfigure (self.tr_item, trace = args, ) def order_traces (self): """ self.order_traces() is a method for ordering the traces displayed from top to bottom in alphabetical order according to the trace labels Returns ------- traces: ordered list of trace handles """ self.traces.sort(key = lambda key: key[1]['label']) traces = [key[0] for key in self.traces] return (traces) def test_data (self, tstart, tend): """ self.test_data(tstart, tend) is a method that is called to create a test data trace Parameters ---------- tstart: time at beginning of the trace tend: time at end of the trace Returns ------- vec: trace buvector2 handle string """ samprate = 100.0 dt = 1.0/samprate istart = int((tstart - self.timeleft)*samprate) iend = int((tend - self.timeleft)*samprate) nsamp = iend - istart if nsamp < 1: return None t0 = tstart + istart*dt data = [0.0 for i in range(nsamp)] for i in range(nsamp): t = (i+istart)*dt t /= 10.0 it = int(t) t -= it data[i] = -0.5 + t vec = buvector2_create_tsamp (nsamp, t0, samprate, data) return vec @classmethod def trace_creation_callback (self, pfstring): """ TraceDisplay.trace_creation_callback(pfstring) is a class method that is called by the bptracepanel item widget whenever a new trace has been created and is being displayed. It is called with a single Antelope parameter file string parameter. Parameters ---------- pfstring: Input Antelope parameter file string. This string will contain at a minimum a parameter named 'instance' that corresponds to the instance option, specified in the trace_creation_callback= option when the bptracepanel item was created, and can be used to obtain the specific object instance. This parameter file string will also have a single associative array with name set to the trace handle string and the array values set to the various trace parameters, such as trace label, trace y value and trace height value. Returns ------- ret: an output string set to 'ok' should normally be return """ argsd = PfSubs.pfstring2py (pfstring) instance = int(argsd['instance']) obj = TraceDisplay.instances[instance] keys = argsd.keys() for key in keys: if key == 'instance': continue else: trace_handle = key break dataspc = 'type=vector,vec=%s,trace=%s' % (obj.vec, trace_handle) obj.dataspcs[trace_handle] = dataspc obj.canvas.itemconfigure (obj.tr_item, data = dataspc) obj.traces.append((trace_handle, argsd[trace_handle])) traces = {'traces': obj.order_traces()} obj.canvas.itemconfigure (obj.tr_item, traces = PfSubs.py2pfstring(traces)) return "ok" @classmethod def repixelate_callback (self, pfstring): """ TraceDisplay.repixelate_callback(pfstring) is a class method that is called by the bptracepanel item widget whenever the time scales or the pixmap width have changed and a repixelation of the display needs to be rendered. It is called with a single Antelope parameter file string parameter. Parameters ---------- pfstring: Input Antelope parameter file string. This string will contain at a minimum a parameter named 'instance' that corresponds to the instance option, specified in the trace_creation_callback= option when the bptracepanel item was created, and can be used to obtain the specific object instance. Returns ------- ret: an output string set to 'ok' should normally be return """ argsd = PfSubs.pfstring2py (pfstring) instance = int(argsd['instance']) obj = TraceDisplay.instances[instance] for (trace, params) in obj.traces: obj.canvas.itemconfigure (obj.tr_item, data = obj.dataspcs[trace] ) return "ok" if len(sys.argv) != 1: print "usage: test_bptracepanel" exit (1) plot = TraceDisplay() plot.mw.mainloop()
Following is an example of using a bptracepanel item extension to display data from an ORB.
#!/opt/antelope/python2.7.6/bin/python import os import sys import signal signal.signal(signal.SIGINT, signal.SIG_DFL) sys.path.append(os.environ['ANTELOPE'] + "/data/python") import antelope.elog as elog import antelope.stock as stock from antelope.buplot import * from antelope.buvector import * class PfSubs(object): """ PfSubs is a set of class methods for converting Antelope parameter file strings to/from python dictionaries """ def __init__ (self): return @classmethod def pfstring2py (self, pfstring, pyd=None): """ PfSubs.pfstring2py(pfstring, pyd=None) is a class method for converting Antelope parameter file strings to python dictionaries Parameters ---------- pfstring: input Antelope parameter file string pyd: optional input python dictionary object. If specified then the output phython disctionary object is formed by appending the Antelope parameter file string to the input object Returns ------- pyd: output python dictionary object """ pf = stock.ParameterFile() pf.pfcompile (pfstring) pyf = pf.pf2dict() if pyd != None: pyd.update(pyf) else: pyd = pyf return (pyd) @classmethod def py2pfstring (self, pyd): """ PfSubs.py2pfstring(pyd) is a class method for converting python dictionaries to Antelope parameter file strings Parameters ---------- pyd: input python dictionary object Returns ------- pfstring: output Antelope parameter file string """ pf = stock.ParameterFile() pf['foo'] = pyd pfstr = pf.pf2string() pfstr = pfstr[4:-2] return pfstr class TraceDisplay(object): """ TraceDisplay(orbname, select) is the main trace display class. Parameters ---------- orbname: input ORB name select: input ORB select expression """ instance = -1 instances = {} def __init__ (self, orbname, select): """ set up an object instance counter and an object instance dictionary """ TraceDisplay.instance += 1 TraceDisplay.instances[TraceDisplay.instance] = self """ grab buplot main window """ self.mw = Buplot().mw """ set root window title and geometry """ self.mw.wm_title("test_bptracepanel") self.mw.geometry ("1000x1000") self.mw.grid_rowconfigure ( 0 , weight = 1 ) self.mw.grid_columnconfigure ( 0 , weight = 1 ) """ set 80 pixel left margin where the channel labels willl be displayed """ self.mleft = 80 """ set time window """ self.twin = 86400.0 """ create stock tkinter canvas - note that dimensions are not set, they will slave to the root window """ self.canvas = BCanvas ( self.mw, borderwidth = 0, highlightthickness = 0, background = "#800000" ) """ and this is what slaves the canvas dimensions to the root window """ self.canvas.grid_configure ( column = 0, row = 0, sticky = 'nsew') """ create a buplot viewport as a canvas item and set the left margin, note that since the viewport dimensions were not specified, the will slave to the canvas dimensions """ self.vp_item = self.canvas.create_bpviewport ("tracevp", 0, 0, mleft = self.mleft, mright = 0, mbottom = 0, mtop = 0, fill = '#e0e0e0', fill_frame = 'lightblue', tag = "tracevp") """ create the buplot tracepanel as a canvas item, set the new_channel_callback, trace_creation_callback and repixelate_callback to TraceDisplay class methods (note the use of the class instance variable that will be used by the class method callback in instantiate the object making the callback), set the time scales to slave to real-time through the timefuture and timeupdate_interval options, set the panel and pixmap plot scales and dimensions so they are slaved to the viewport (this will cause these to automatically adjust to changing window dimensions) """ self.tr_item = self.canvas.create_bptracepanel ("tracevp", new_channel_callback = 'TraceDisplay.new_channel_callback %d' % TraceDisplay.instance, trace_creation_callback = 'TraceDisplay.trace_creation_callback %d' % TraceDisplay.instance, repixelate_callback = 'TraceDisplay.repixelate_callback %d' % TraceDisplay.instance, twin = self.twin, timefuture = 300.0, timeupdate_interval = 10.0, foreground_tmark_now = 'red', rerenderupdate_interval = 1.0, label_font = 'courier 5', panel_height = 'viewport', pixmap_height = 'viewport', panel_width = 'viewport', pixmap_width = 'viewport' ) """ initialize an internal object list of traces and a dictionary for holding the data input specifications for each trace """ self.traces = [] self.dataspcs = {} """ Launch a single ORB source thread. """ args = 'orb=dataorb,orbname=%s,accept=%s,after=%.1f' % (orbname, select, stock.now()-86400.0-300.0) self.canvas.itemconfigure (self.tr_item, orb = args ) def order_traces (self): """ self.order_traces() is a method for ordering the traces displayed from top to bottom in alphabetical order according to the trace labels Returns ------- traces: ordered list of trace handles """ self.traces.sort(key = lambda key: key[1]['label']) traces = [key[0] for key in self.traces] return (traces) @classmethod def new_channel_callback (self, pfstring): """ TraceDisplay.new_channel_callback(pfstring) is a class method that is called by the bptracepanel item widget whenever a new data SEED channel is read by the ORB data source thread. It is called with a single Antelope parameter file string parameter. This callback services new channels by adding them as new traces using the trace option. Parameters ---------- pfstring: Input Antelope parameter file string. This string will contain a parameter named 'instance' that corresponds to the instance option, specified in the new_channel_callback= option when the bptracepanel item was created, and can be used to obtain the specific object instance. This parameter file string will also have a parameter named 'channel' which contains the new data channel specified in the form <orbtag>:<net_sta_chan_loc>. Returns ------- ret: an output string set to 'ok' should normally be return """ argsd = PfSubs.pfstring2py (pfstring) instance = int(argsd['instance']) obj = TraceDisplay.instances[instance] (orbtag, nscl) = argsd['channel'].split(':') args = 'orb=%s,nscl=%s,label=%s,y=auto,h=fit' % (orbtag, nscl, nscl) obj.canvas.itemconfigure (obj.tr_item, trace = args) return "ok" @classmethod def trace_creation_callback (self, pfstring): """ TraceDisplay.trace_creation_callback(pfstring) is a class method that is called by the bptracepanel item widget whenever a new trace has been created and is being displayed. It is called with a single Antelope parameter file string parameter. This callback saves the returned trace handle string, along with the set of trace parameters (which contains the trace labels for sorting) and re-arranges the traces vertically according to trace label alphabetic order. Parameters ---------- pfstring: Input Antelope parameter file string. This string will contain at a minimum a parameter named 'instance' that corresponds to the instance option, specified in the trace_creation_callback= option when the bptracepanel item was created, and can be used to obtain the specific object instance. This parameter file string will also have a single associative array with name set to the trace handle string and the array values set to the various trace parameters, such as trace label, trace y value and trace height value. Returns ------- ret: an output string set to 'ok' should normally be return """ argsd = PfSubs.pfstring2py (pfstring) instance = int(argsd['instance']) obj = TraceDisplay.instances[instance] keys = argsd.keys() for key in keys: if key == 'instance': continue else: trace_handle = key break obj.traces.append((trace_handle, argsd[trace_handle])) traces = {'traces': obj.order_traces()} obj.canvas.itemconfigure (obj.tr_item, traces = PfSubs.py2pfstring(traces)) return "ok" @classmethod def repixelate_callback (self, pfstring): """ TraceDisplay.repixelate_callback(pfstring) is a class method that is called by the bptracepanel item widget whenever the time scales or the pixmap width have changed and a repixelation of the display needs to be rendered. It is called with a single Antelope parameter file string parameter. This callback services repixelations by rewinding the input ORB read pointer to replay the data packets. Parameters ---------- pfstring: Input Antelope parameter file string. This string will contain at a minimum a parameter named 'instance' that corresponds to the instance option, specified in the trace_creation_callback= option when the bptracepanel item was created, and can be used to obtain the specific object instance. Returns ------- ret: an output string set to 'ok' should normally be return """ argsd = PfSubs.pfstring2py (pfstring) instance = int(argsd['instance']) obj = TraceDisplay.instances[instance] args = 'orb=dataorb,after=%.1f' % (stock.now()-86400.0-300.0) obj.canvas.itemconfigure (obj.tr_item, orb = args ) return "ok" if len(sys.argv) != 1 and len(sys.argv) != 3: print "usage: test_bptracepanel_orb [orbname select_expr]" exit (1) orbname = 'bbarray.ucsd.edu:gsn' select = '.*_LHZ_00.*' if len(sys.argv) == 3: orbname = sys.argv[1] select = sys.argv[2] plot = TraceDisplay(orbname, select) plot.mw.mainloop()
Following is an example of using a bptracepanel item extension to display data from a Datascope database.
#!/opt/antelope/python2.7.6/bin/python import os import sys import signal signal.signal(signal.SIGINT, signal.SIG_DFL) sys.path.append(os.environ['ANTELOPE'] + "/data/python") import antelope.elog as elog import antelope.stock as stock from antelope.buplot import * from antelope.buvector import * from ttk import * import math class PfSubs(object): """ PfSubs is a set of class methods for converting Antelope parameter file strings to/from python dictionaries """ def __init__ (self): return @classmethod def pfstring2py (self, pfstring, pyd=None): """ PfSubs.pfstring2py(pfstring, pyd=None) is a class method for converting Antelope parameter file strings to python dictionaries Parameters ---------- pfstring: input Antelope parameter file string pyd: optional input python dictionary object. If specified then the output phython disctionary object is formed by appending the Antelope parameter file string to the input object Returns ------- pyd: output python dictionary object """ pf = stock.ParameterFile() pf.pfcompile (pfstring) pyf = pf.pf2dict() if pyd != None: pyd.update(pyf) else: pyd = pyf return (pyd) @classmethod def py2pfstring (self, pyd): """ PfSubs.py2pfstring(pyd) is a class method for converting python dictionaries to Antelope parameter file strings Parameters ---------- pyd: input python dictionary object Returns ------- pfstring: output Antelope parameter file string """ pf = stock.ParameterFile() pf['foo'] = pyd pfstr = pf.pf2string() pfstr = pfstr[4:-2] return pfstr class TraceDisplay(object): """ TraceDisplay(dbname, select) is the main trace display class. Parameters ---------- dbname: input database name select: input database select expression """ instance = -1 instances = {} def __init__ (self, dbname, select): """ set up an object instance counter and an object instance dictionary """ TraceDisplay.instance += 1 TraceDisplay.instances[TraceDisplay.instance] = self """ grab buplot main window """ self.mw = Buplot().mw """ set root window title and geometry """ self.mw.wm_title("test_bptracepanel") self.mw.geometry ("1000x1000") self.mw.grid_rowconfigure ( 0 , weight = 1 ) self.mw.grid_columnconfigure ( 0 , weight = 1 ) """ set 120 pixel left margin where the channel labels willl be displayed """ self.mleft = 120 """ set time window """ self.twin = 86400.0 """ create stock tkinter canvas - note that dimensions are not set, they will slave to the root window """ self.canvas = BCanvas ( self.mw, borderwidth = 0, highlightthickness = 0, background = "#800000" ) """ and this is what slaves the canvas dimensions to the root window """ self.canvas.grid_configure ( column = 0, row = 0, sticky = 'nsew') """ create a buplot viewport as a canvas item and set the left margin, note that since the viewport dimensions were not specified, the will slave to the canvas dimensions """ self.vp_item = self.canvas.create_bpviewport ("tracevp", 0, 0, mleft = self.mleft, mright = 0, mbottom = 0, mtop = 0, fill = '#e0e0e0', fill_frame = 'lightblue', tag = "tracevp") """ create the buplot tracepanel as a canvas item, set the new_channel_callback, to TraceDisplay class methods (note the use of the class instance variable that will be used by the class method callback in instantiate the object making the callback), set the time scales to slave to real-time through the timefuture and timeupdate_interval options, set the panel and pixmap plot scales and dimensions so they are slaved to the viewport, this will cause these to automatically adjust to changing window dimensions) """ self.tr_item = self.canvas.create_bptracepanel ("tracevp", new_channel_callback = 'TraceDisplay.new_channel_callback %d' % TraceDisplay.instance, panel_height = 'viewport', pixmap_height = 'viewport', panel_width = 'viewport', pixmap_width = 'viewport' ) """ Launch a single database source thread. """ if select != None and select != '': args = 'db=datadb,dbname=%s,accept=%s' % (dbname, select) else: args = 'db=datadb,dbname=%s' % dbname self.canvas.itemconfigure (self.tr_item, db = args ) @classmethod def new_channel_callback (self, pfstring): """ TraceDisplay.new_channel_callback(pfstring) is a class method that is called by the bptracepanel item widget whenever a new database is opened by the database data source thread. It is called with a single Antelope parameter file string parameter. This callback services new channels by adding them as new traces using the trace option. Parameters ---------- pfstring: Input Antelope parameter file string. This string will contain a parameter named 'instance' that corresponds to the instance option, specified in the new_channel_callback= option when the bptracepanel item was created, and can be used to obtain the specific object instance. This parameter file string will also have a parameter named 'dbtag' which contains the new database tag name for the new data channels. The new CSS sta and chan codes, along with their corresponding SEED net_sta_chan_loc codes and time ranges for each channel are contained in a table named 'dbchannels'. Returns ------- ret: an output string set to 'ok' should normally be return """ argsd = PfSubs.pfstring2py (pfstring) instance = int(argsd['instance']) obj = TraceDisplay.instances[instance] channels = argsd['dbchannels'] dbtag = argsd['dbtag'] tleft = None tright = None for channel in channels: (sta, chan, nscl, tstart, tend) = channel.split() tstart = float(tstart) tend = float(tend) if tleft == None or tstart < tleft: tleft = tstart if tright == None or tend > tright: tright = tend args = 'db=%s,sc=%s,label=%s,y=auto,h=fit' % (dbtag, "%s %s" % (sta, chan), "%s %s" % (sta, chan)) obj.canvas.itemconfigure (obj.tr_item, trace = args) obj.twin = (tright - tleft) * 1.1 obj.timeright = tright + 0.05 * obj.twin obj.canvas.itemconfigure (obj.tr_item, timeright = obj.timeright, twin = obj.twin) return "ok" if len(sys.argv) != 1 and len(sys.argv) != 3: print "usage: test_bptracepanel_db [dbname select_expr]" exit (1) dbname = '/opt/antelope/data/db/demo/demo' select = '' if len(sys.argv) == 3: dbname = sys.argv[1] select = sys.argv[2] plot = TraceDisplay(dbname, select) plot.mw.mainloop()