I have created a minimal version that does almost nothing except plot a hard-coded data set and allow you to change the axis settings. It does however use the actual gf4 code to handle the data and plotting, just with everything else removed.
Put the attached file in the same directory where you have gf4, and run it from a console window. You can change the axes scale type from the "Plot" menu. If log-log still does not display a log-log plot, at least we will have something much simpler to work with. On Tuesday, May 17, 2022 at 10:38:07 PM UTC-4 tbp1...@gmail.com wrote: > Here is another test you can do. Enable the Test menu: > > in createmenus.py, find "test menu". Uncomment the last line > (mainMenu.add_cascade(...). > > Then navigate to @file gf4.pyw-->class > PlotManager(AbstractPlotManager)-->Misc-->testMacro. Add a new last line > to the string > so that it reads > > def testMacro(self): > self.runMacro('''dsin > copy2buff > sqr > plot > ; comment > overplotbuf > # another comment > loglog > ''') > > This adds the command "loglog" to the macro definition. Save everything, > then re-open gf4. there should be a new "Test" menu. select the "Run > Macro" item. It will create a waveform and do things with it, and the last > step should switch the axes to log-log. This command is the same one that > is (or should be) sent from the button window when you press the "loglog" > key. > > You can also verify that the "loglog" button is hooked up to the right > command string. Run the cmdwin.py file as its own program from the command > line. This produces a button window that is exactly what you see when you > run gf4, except that when you press a button, its command is printed to the > console instead of getting executed. > > On Tuesday, May 17, 2022 at 7:57:17 PM UTC-4 tbp1...@gmail.com wrote: > >> Strange. Yes, let's zoom. email me at the address you have so we can >> see what's what. in the meantime, do the semilogX and semilogY buttons >> seem to change the scales as expected? >> >> I hope that matplotlib didn't change something. My version should be >> very current since i just installed python 3.10. also, my python 3.7 >> version, which is several years old, also works as expected. my versions: >> >> Python matplotlib >> 3.10 3.5.2 >> 3.9 3.5.0 >> 3.7 3.5.2 # hmm, must have updated it recently >> but even back when gf4 ran under Python 2.7, loglog plotting worked as >> expected. >> >> On Tuesday, May 17, 2022 at 7:20:11 PM UTC-4 Edward K. Ream wrote: >> >>> On Tue, May 17, 2022 at 4:05 PM tbp1...@gmail.com <tbp1...@gmail.com> >>> wrote: >>> >>>> yellow, that's good! now as long as they flash when clicked... >>>> >>> >>> Yes. All buttons flash when clicked. >>> >>> Traces show that plot is called, but neither the scales nor the plot >>> itself change after clicking LogLog. Perhaps we could zoom sometime. >>> >>> Edward >>> >> -- You received this message because you are subscribed to the Google Groups "leo-editor" group. To unsubscribe from this group and stop receiving emails from it, send an email to leo-editor+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/leo-editor/3bfe41e7-134b-466d-bc17-f48b68d65d00n%40googlegroups.com.
#@+leo-ver=5-thin #@+node:tom.20220518082230.1: * @file gf_min.py #@@language python #@@tabwidth -4 #@+others #@+node:tom.20220518082247.1: ** imports import os.path import sys import tkinter as Tk import tkinter.font as tkFont from tkinter import filedialog as fd import matplotlib from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk from matplotlib.figure import Figure from entry import TextFade #from randnum import * from AbstractPlotMgr import AbstractPlotManager from AbstractPlotMgr import MAIN, BUFFER, STACKDEPTH, ERRBAND_HI, ERRBAND_LO from colors import (BLACK, CYAN, GRAY, LIGHTGRAY, DEFAULTGRIDCOLOR, ColorBgPairs) from Dataset import Dataset from Linestyle import (Linestyle, LINETHIN, LINEMED) matplotlib.use('TkAgg') #@+node:tom.20220518082506.1: ** Declarations COMMENTS = (';', '#') ENCODING = 'utf-8' #@+node:tom.20220518082700.1: ** class PlotManager(AbstractPlotManager) class PlotManager(AbstractPlotManager): # pylint: disable = import-outside-toplevel # pylint: disable = too-many-public-methods # Putting these imports at the top of the module doesn't work # Because they expect to be called as methods with a "self" param. from Plot import plot from Timehack import timehack from BuildCommands import buildCommands from MakeWaveforms import ( makeExponential, makeSine, makeDampedSine, makeStep, makeDelta, makeRamp, makeSquarewave, makeRandomNoise, makeUniformNoise, makeGaussianNoise, pdfGaussian, cdfGaussian) #@+others #@+node:tom.20220518082700.2: *3* Decorators #@+node:tom.20220518082700.3: *4* doErrorBands def doErrorBands(method): '''A decorator method to process error bands in the same way that the y data is processed. The original method call must return the Dataset it is being applied to, and the method it applies. ''' # pylint: disable = no-self-argument # pylint: disable = not-callable def new_method(*args): # args[0] will be the calling instance (i.e., self) # Call original method _ds, func = method(args[0]) if _ds and _ds.errorBands: func(_ds.errorBands[0]) func(_ds.errorBands[1]) return new_method #@+node:tom.20220518082700.4: *4* REQUIRE_MAIN def REQUIRE_MAIN(procedure): """A decorator method to check if there is data in the MAIN slot. If not, exit procedure with error message. Otherwise, execute the procedure. ARGUMENT procedure -- an instance method that takes no parameters. """ # pylint: disable = no-self-argument # pylint: disable = not-callable def new_proc(*args): # args[0] will be the calling instance (i.e., self) self = args[0] _main = self.stack[MAIN] if not (_main and len(_main.xdata)): msg = 'Missing Waveform' self.announce(msg) self.flashit() return procedure(*args) return new_proc #@+node:tom.20220518082700.5: *4* REQUIRE_MAIN_BUFF def REQUIRE_MAIN_BUFF(procedure): """A decorator method to check if there is data in the MAIN and BUFFER slots. If not, exit procedure with error message. Otherwise, execute the procedure. ARGUMENT procedure -- an instance method that takes no parameters. """ # pylint: disable = no-self-argument # pylint: disable = not-callable def new_proc(*args): # args[0] will be the calling instance (i.e., self) self = args[0] _main = self.stack[MAIN] _buff = self.stack[BUFFER] if not (_main and len(_main.xdata) and _buff and len(_buff.xdata)): self.announce("Missing one or both waveforms") self.flashit() return procedure(self) return new_proc #@+node:tom.20220518082700.6: *3* __init__ def __init__(self, root=None): super().__init__(root) self.toolbar = None self.linestyles = [Linestyle() for i in range(self.stackdepth)] self.linestyles[MAIN].linecolor = BLACK self.linestyles[MAIN].sym_mec = BLACK self.linestyles[MAIN].linewidth = LINEMED self.linestyles[BUFFER].linecolor = CYAN self.linestyles[BUFFER].sym_mec = BLACK self.linestyles[BUFFER].sym_mfc = CYAN self.linestyles[BUFFER].linewidth = LINEMED self.errorbar_linestyles = Linestyle() self.errorbar_linestyles.linecolor = GRAY self.errorbar_linestyles.linewidth = LINETHIN self.main_symbol_color = Tk.StringVar() self.buffer_symbol_color = Tk.StringVar() self.main_line_color = Tk.StringVar() self.buffer_line_color = Tk.StringVar() self.radio_main_linestyle = Tk.StringVar() self.radio_buffer_linestyle = Tk.StringVar() self.main_symbol_shape = Tk.StringVar() self.buffer_symbol_shape = Tk.StringVar() self.graph_bg_color = Tk.StringVar() self.initpath = '.' # for File Dialog directory self.current_path = '' self.setMenus() #@+node:tom.20220518082700.7: *3* setMenus def setMenus(self): mainMenu = Tk.Menu(self.root) plotSubmenu = Tk.Menu(self.root) commands = ( ("Plot", self.plotmain, 0), ) for label, command, underline in commands: plotSubmenu.add_command(label = label, command = command, underline = underline) plotSubmenu.add_separator() commands = ( ('Lin-lin', self.setLinLin), ('Semilog(Y)', self.setSemilogY), ('Semilog(X)', self.setSemilogX), ('Log-log', self.setLogLog) ) for label, command in commands: plotSubmenu.add_radiobutton(label = label, command = command) mainMenu.add_cascade(label='Plot', menu=plotSubmenu) self.root.config(menu=mainMenu) #@+node:tom.20220518082700.9: *3* Widget Utilities #@+node:tom.20220518082700.11: *4* setWindowTitle def setWindowTitle(self, title=''): if self.root: self.root.wm_title(title) #@+node:tom.20220518082700.12: *4* label_select_all def label_select_all(self, event): '''Select all text in an edit widget (Tk.Entry) when <CNTRL-A> is pressed. It seems that the default key is <CTRL-/> (standard for linux) and this overrides that. The "return 'break'" is essential, otherwise this gets ignored. ''' event.widget.selection_range(0, Tk.END) return 'break' #@+node:tom.20220518082700.13: *4* setupFigure def setupFigure(self, title='GF4'): root = Tk.Tk() root.option_add('*tearOff', False) # Tk specific menu option root.bind('<Alt-F4>', self.quit) f = Figure(figsize=(9, 6), dpi=100, facecolor=LIGHTGRAY) ax = None # f.add_subplot(111, axisbg='0.3') canvas = FigureCanvasTkAgg(f, master=root) self.toolbar = NavigationToolbar2Tk(canvas, root) canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) self.toolbar.pack() self._sv = Tk.StringVar() self._sv.set('') self.editWidget = Tk.Entry(root, textvariable=self._sv, width=80) self.editWidget.bind('<Return>', self.doneEditLabel) self.editWidget.bind('<Escape>', self.doneEditLabel) self.editWidget.bind('<Control-a>', self.label_select_all) self.root = root self.axes = ax self.figure = f self.canvas = canvas self.fix_ticks() self.setWindowTitle(title) canvas.mpl_connect('button_press_event', self.edit_label) self.toolbar.update() _ann = TextFade(root, height=1, font='size 12', width=100, bd=0, pady=5, bg='LightBlue') _ann.insert(1.0, '') _ann.config(state=Tk.DISABLED) _ann.pack() self.announcer = _ann self.set_editable_labels() self.currentLabelEditing = None #@+node:tom.20220518082700.14: *4* fadeit def fadeit(self, widget=None): '''For TextFade widgets and similar with a fade() method.''' if widget and 'fade' not in dir(widget): return if not widget: widget = self.announcer widget.fade() #@+node:tom.20220518082700.15: *4* flashit def flashit(self, color='yellow', widget=None): '''For TextFade widgets and similar with a flash() method.''' if widget and 'flash' not in dir(widget): return if not widget: widget = self.announcer widget.flash(color) widget.after(1000) self.fadeit(widget) #@+node:tom.20220518082700.16: *4* announce def announce(self, msg='This is a test'): '''Show an message in the announcement area.''' _ann = self.announcer _ann.config(state=Tk.NORMAL) _ann.delete(1.0, Tk.END) _ann.insert(1.0, msg) _ann.config(state=Tk.DISABLED) #@+node:tom.20220518082700.17: *4* set_editable_labels def set_editable_labels(self): '''Create a list of those labels that can be edited with the standard single-line edit widget. Return nothing. ''' if not self.axes: return self.editableLabels.extend([ self.axes.get_xaxis().get_label(), self.axes.get_yaxis().get_label()] ) #@+node:tom.20220518082700.18: *4* edit_label def edit_label(self, event): '''Respond to a mouse-press event. Check all the editable labels, plus other Text items in the figure, to see if the event belongs to one of them. If so, display the label edit widget over the label. ARGUMENT event - a mathplotlib event; MouseEvent is expected. RETURNS nothing. ''' if not self.axes: return _labels = [item for item in self.axes.get_children() if isinstance(item, matplotlib.text.Text)] _labels.extend(self.editableLabels) lab = None for _lab in _labels: if _lab.contains(event)[0]: lab = _lab break if lab is None: self.currentLabelEditing = None return self.currentLabelEditing = lab ew = self.editWidget self._sv.set(lab.get_text()) #@+<< configure editwidget >> #@+node:tom.20220518082700.19: *5* << configure editwidget >> lab_fontsize = int(lab.get_size()) tkfont = tkFont.Font(size=lab_fontsize) bbox = lab.get_window_extent() ul, lr = bbox.get_points() ulx, uly = ul lrx, lry = lr canvas = self.canvas dpi = canvas.figure._dpi canvas_bbox_ul = canvas.figure.bbox_inches.get_points()[1] canv_ulx, canv_uly = \ int(dpi * canvas_bbox_ul[0]), int(dpi * canvas_bbox_ul[1]) ew.configure(font=tkfont) #@-<< configure editwidget >> ew.place(x=ulx, y=canv_uly - lry) ew.selection_clear() ew.focus_set() ew.lift() #@+node:tom.20220518082700.20: *4* doneEditLabel def doneEditLabel(self, event): """Receive <Return> and <Escape> events. For <Escape>, leave. For <Return>, also change the label's text, and set the Dataset's corresponding label, if any. """ if not self.currentLabelEditing: return if event.keysym == 'Return': _newtext = event.widget.get() self.currentLabelEditing.set_text(_newtext) self.canvas.draw() if self.currentLabelEditing is self.axes.get_xaxis().get_label(): self.stack[MAIN].xaxislabel = _newtext elif self.currentLabelEditing is self.axes.get_yaxis().get_label(): self.stack[MAIN].yaxislabel = _newtext elif _newtext == self.axes.get_title(): self.stack[MAIN].figurelabel = _newtext elif event.keysym == 'Escape': self.canvas.draw() event.widget.lower() event.widget.selection_clear() self.currentLabelEditing = None #@+node:tom.20220518082700.21: *3* Data Stack #@+node:tom.20220518082700.22: *4* copyToBuffer def copyToBuffer(self): if not self.stack[MAIN]: self.announce("No waveform to copy") self.flashit() return self.stack[BUFFER] = self.stack[MAIN].copy() #@+node:tom.20220518082700.23: *4* swap_data def swap_data(self): if not self.stack[MAIN] or not self.stack[BUFFER]: self.announce("Missing one or both waveforms - nothing to swap") self.flashit() return _temp = self.stack[BUFFER].copy() self.stack[BUFFER] = self.stack[MAIN].copy() self.stack[MAIN] = _temp #@+node:tom.20220518082700.24: *4* paste_data def paste_data(self): if not self.stack[BUFFER]: self.announce("No waveform to paste") self.flashit() return self.stack[MAIN] = self.stack[BUFFER].copy() #@+node:tom.20220518082700.25: *4* drop_stack def drop_stack(self): '''Drop stack of datasets, leave top one unchanged.''' for i in range(STACKDEPTH - 1): self.stack[i] = self.stack[i + 1].copy() #@+node:tom.20220518082700.26: *4* push_with_copy def push_with_copy(self): '''Push dataset stack up, duplicate duplicate bottom: top-1 -> top -2 -> -1 ... 1 -> 2 0 -> 1 0 stays unchanged ''' for i in range(STACKDEPTH - 1, 0, -1): self.stack[i] = self.stack[i - 1].copy() #@+node:tom.20220518082700.27: *4* rotate_stack_up def rotate_stack_up(self): '''Rotate dataset stack up: 1 -> 2 2 -> 3 ... top -> 1 ''' temp = self.stack[STACKDEPTH - 1].copy() self.push_with_copy() self.stack[0] = temp #@+node:tom.20220518082700.28: *4* rotate_stack_down def rotate_stack_down(self): '''Rotate stack downwards: top -> top-1 ... 2 -> 1 1 -> top ''' temp = self.stack[0].copy() self.drop_stack() self.stack[STACKDEPTH - 1] = temp #@+node:tom.20220518082700.29: *4* copy_to_top def copy_to_top(self): '''Copy active dataset to top of stack.''' self.stack[STACKDEPTH - 1] = self.stack[MAIN].copy() #@+node:tom.20220518082700.30: *4* copy_from_top def copy_from_top(self): '''Copy top dataset to bottom of stack ("main").''' self.stack[MAIN] = self.stack[STACKDEPTH - 1].copy() #@+node:tom.20220518082700.31: *4* store1 def store1(self): """Store X dataset in a special location outside the stack.""" _ds = self.stack[MAIN].copy() self.storage = _ds #@+node:tom.20220518082700.32: *4* recall1 def recall1(self): """Recall stored dataset to X.""" if self.storage: self.stack[MAIN] = self.storage.copy() else: self.announce("No stored data to recall") self.flashit() #@+node:tom.20220518082700.33: *3* Configure Graph #@+node:tom.20220518082700.34: *4* set_axis_bg def set_axis_bg(self): self.axes.set_facecolor('yellow') # ============================================================= #@+node:tom.20220518082700.35: *4* setYMin def setYMin(self, val): if not self.axes: return axis = self.axes axis.set_ybound(self.stack[MAIN].ymin) #@+node:tom.20220518082700.36: *4* setSemilogX def setSemilogX(self): self.semilogY = False self.semilogX = True self.plot() #@+node:tom.20220518082700.37: *4* setSemilogY def setSemilogY(self): self.semilogY = True self.semilogX = False self.plot() #@+node:tom.20220518082700.38: *4* fix_ticks def fix_ticks(self): '''Make tick marks point out of the figure's frame rather than the default of inwards. ''' if not self.axes: return # tick mark adjustments adapted from # http://osdir.com/ml/python.matplotlib.general/2005-01/msg00076.html axis = self.axes xticks = axis.get_xticklines() for tick in xticks: tick.set_markersize(6) yticks = axis.get_yticklines() for tick in yticks: tick.set_markersize(6) xlabels = axis.get_xticklabels() for label in xlabels: label.set_y(-0.02) label.set_size('small') ylabels = axis.get_yticklabels() for label in ylabels: label.set_x(-0.02) label.set_size('small') #@+node:tom.20220518082700.39: *4* setLogLog def setLogLog(self): self.semilogY = True self.semilogX = True self.plot() #@+node:tom.20220518082700.40: *4* setLinLin def setLinLin(self): self.semilogY = False self.semilogX = False self.plot() #@+node:tom.20220518082700.41: *4* setPlotLineWidth def setPlotLineWidth(self, position, width): self.linestyles[position].set_linewidth(width) #@+node:tom.20220518082700.42: *4* setLineColor def setLineColor(self, stackpos): '''Called only though a menu selection, so that self.x_line_color is set before this method is called. ''' if stackpos == MAIN: _color = self.main_line_color.get() else: _color = self.buffer_line_color.get() self.linestyles[stackpos].set_linecolor(_color) #@+node:tom.20220518082700.43: *4* setLineColorMain def setLineColorMain(self): self.setLineColor(MAIN) #@+node:tom.20220518082700.44: *4* setLineColorBuffer def setLineColorBuffer(self): self.setLineColor(BUFFER) #@+node:tom.20220518082700.45: *4* setSymColor def setSymColor(self, stackpos): '''Called only though a menu selection, so that self.x_symbol_color is set before this method is called. ''' if stackpos == MAIN: _color = self.main_symbol_color.get() else: _color = self.buffer_symbol_color.get() self.linestyles[stackpos].set_sym_color(_color) #@+node:tom.20220518082700.46: *4* setSymColorMain def setSymColorMain(self): self.setSymColor(MAIN) #@+node:tom.20220518082700.47: *4* setSymColorBuffer def setSymColorBuffer(self): self.setSymColor(BUFFER) #@+node:tom.20220518082700.48: *4* setBgColor def setBgColor(self): _color = self.graph_bg_color.get() self.axes.set_facecolor(_color) _gridcolor = ColorBgPairs.get(_color, DEFAULTGRIDCOLOR) self.gridcolor = _gridcolor #@+node:tom.20220518082700.49: *4* setMainLineWidth def setMainLineWidth(self): _width = float(self.radio_main_linestyle.get()) self.setPlotLineWidth(MAIN, _width) #@+node:tom.20220518082700.50: *4* setBufferLineWidth def setBufferLineWidth(self): _width = float(self.radio_buffer_linestyle.get()) self.setPlotLineWidth(BUFFER, _width) #@+node:tom.20220518082700.51: *4* setMarkerStyle def setMarkerStyle(self, stackpos): # pylint: disable = no-member if stackpos == MAIN: _ms = self.main_marker_style else: _ms = self.buffer_marker_style _style = int(_ms.get()) _ls = self.linestyles[stackpos] if _style == 1: _ls.useLine = True _ls.useSym = False elif _style == 2: _ls.useLine = False _ls.useSym = True else: _ls.useLine = True _ls.useSym = True #@+node:tom.20220518082700.52: *4* setMainMarkerStyle def setMainMarkerStyle(self): self.setMarkerStyle(MAIN) #@+node:tom.20220518082700.53: *4* setBufferMarkerStyle def setBufferMarkerStyle(self): self.setMarkerStyle(BUFFER) #@+node:tom.20220518082700.54: *4* setSymShape def setSymShape(self, stackpos): if stackpos == MAIN: _shp = self.main_symbol_shape else: _shp = self.buffer_symbol_shape _shape = _shp.get() _ls = self.linestyles[stackpos] _ls.set_sym_style(_shape) #@+node:tom.20220518082700.55: *4* setSymShapeMain def setSymShapeMain(self): self.setSymShape(MAIN) #@+node:tom.20220518082700.56: *4* setSymShapeBuffer def setSymShapeBuffer(self): self.setSymShape(BUFFER) #@+node:tom.20220518082700.57: *4* setXlabel def setXlabel(self, label=''): self.axes.set_xlabel(label) #@+node:tom.20220518082700.58: *4* setYlabel def setYlabel(self, label=''): self.axes.set_ylabel(label) #@+node:tom.20220518082700.59: *4* setFigureTitle def setFigureTitle(self, title=''): #self.axes.set_title(title, size='x-large', y=1.025) self.axes.set_title(title, size='large', y=1.025) #@+node:tom.20220518082700.60: *3* Data Load/Save #@+node:tom.20220518082700.61: *4* set_data def set_data(self, dataset, stackpos=MAIN): self.stack[stackpos] = dataset.copy() #@+node:tom.20220518082700.62: *4* save_data def save_data(self, saveas=True): '''Write x,y data in MAIN to an ASCII file. Labels and other meta data are also written. ARGUMENT saveas -- if True, use Save As dialog. Otherwise, use original filename if one exists, and use Save As instead. RETURNS nothing ''' opt = {} if self.stack[MAIN].orig_filename: opt['initialfile'] = self.stack[MAIN].orig_filename if self.initpath: opt['initialdir'] = self.initpath if saveas: fname = fd.asksaveasfilename(**opt) else: fname = self.stack[MAIN].orig_filename or \ fd.asksaveasfilename(**opt) self.stack[MAIN].writeAsciiData(fname) self.stack[MAIN].orig_filename = fname #@+node:tom.20220518082700.63: *4* load_data def load_data(self, stackpos=MAIN): '''Open a file dialog for reading, and remember its directory and filename. Use that last stored directory or file as the initial directory when opening the file dialog. Load the data from the selected file into the specified Dataset. Assumes data is in ASCII format. Plot the data if no other data has yet been plotted. If there are more than two numeric columns, show a dialog for the user to select the two columns to use. The data set may be divided into parts by the special comment string ';;ENDDATASET'. If so, load each such delineated part into stack positions MAIN, BUFFER, and the top-most position, if there are enough data parts. ARGUMENT stackpos -- integer specifying the stack position to load the data into. RETURNS Nothing. ''' if not self.current_path: f = fd.askopenfile(mode='r', initialdir=self.initpath) else: f = fd.askopenfile(mode='r', initialfile=self.current_path) if not f: return fname = f.name data = f.read() f.close() self.initpath = os.path.dirname(fname) self.current_path = fname blocks = data.split('ENDDATASET') numblocks = len(blocks) if numblocks < 1: self.announce('No data found') return for n in range(min(len(blocks), STACKDEPTH)): block = blocks[n] if not block.split(): continue lines = block.split('\n') _data = Dataset() _data.orig_filename = fname err = _data.setAsciiData(lines, root = self.root) if err: self.announce(f'No data in block {n}') self.flashit() self.announce(f'No data in block {n}') return if n <= BUFFER: self.set_data(_data, n) elif n == BUFFER + 1: self.set_data(_data, STACKDEPTH - 1) if not self.axes: self.plot() #@+node:tom.20220518082700.64: *4* load_plot_data def load_plot_data(self, fname, overplot=False): '''Load the data from the specified file into the specified Dataset. Load the dataset into the MAIN stack buffer position. Assumes data is in ASCII format. Plot the data if no other data has yet been plotted, otherwise plot or overplot it according to the value of the overplot parameter. ARGUMENT fname -- path to a file overplot -- Boolean RETURNS Nothing. ''' with open(fname, encoding = ENCODING) as f: data = f.read() self.initpath = os.path.dirname(fname) blocks = data.split('ENDDATASET') numblocks = len(blocks) if numblocks < 1: self.announce('No data found') return for n in range(min(len(blocks), STACKDEPTH)): block = blocks[n] lines = block.split('\n') _data = Dataset() _data.orig_filename = fname err = _data.setAsciiData(lines) if err: self.announce('%s' % err) self.flashit() self.announce('%s' % err) return if n <= BUFFER: self.set_data(_data, n) elif n == BUFFER + 1: self.set_data(_data, STACKDEPTH - 1) if not self.axes: self.plot() else: if overplot: self.overplot() else: self.plot() #@+node:tom.20220518082700.111: *3* Plot Operations #@+node:tom.20220518082700.112: *4* overplotbuff def overplotbuff(self): self.overplot(BUFFER) #@+node:tom.20220518082700.113: *4* overplot_errorbands def overplot_errorbands(self, stackposition=MAIN): if not self.stack[stackposition].errorBands: self.announce('No errorband data to plot') self.flashit() return _ds = self.stack[stackposition] # Overplot error bands upper = _ds.errorBands[ERRBAND_HI] lower = _ds.errorBands[ERRBAND_LO] del self.stack[STACKDEPTH:] self.stack.append(upper) self.stack.append(lower) self.axes.fill_between(self.stack[MAIN].xdata, self.stack[STACKDEPTH + ERRBAND_HI].ydata, self.stack[STACKDEPTH + ERRBAND_LO].ydata, facecolor='lightgrey', alpha=0.1) for g in [STACKDEPTH + ERRBAND_HI, STACKDEPTH + ERRBAND_LO]: self.overplot(g) #@+node:tom.20220518082700.114: *4* overplot def overplot(self, stackposition=MAIN): self.plot(stackposition, False) #@+node:tom.20220518082700.131: *3* Trend #@+node:tom.20220518082700.139: *3* Misc #@+node:tom.20220518082700.144: *4* runMacro def runMacro(self, cmdlist=''): '''Given a string of commands, one per line, execute the commands in order. First check to make sure all commands are valid; execute the sequence of commands if they are. Lines whose first non-whitespace character is ';' or '#' are ignored as comment lines. Return nothing. ARGUMENT cmdlist -- a string of line-separated command names. RETURNS nothing ''' if not cmdlist: self.announce('No command list to run') self.flashit() return cmds = cmdlist.splitlines() cmds = [c.strip() for c in cmds if c.strip()] cmds = [c for c in cmds if c[0] not in COMMENTS] unknowns = [] for cmd in cmds: if not self.commands.get(cmd): unknowns.append(cmd) if unknowns: msg = 'unknown commands: %s' % (' '.join(unknowns)) self.announce(msg) self.flashit() return for cmd in cmds: self.interpret(cmd) #@+node:tom.20220518082700.145: *4* testMacro def testMacro(self): self.runMacro('''dsin copy2buff sqr plot ; comment overplotbuf # another comment loglog ''') #@+node:tom.20220518082700.147: *4* hasToplevel def hasToplevel(self): for c in self.root.winfo_children(): if c.winfo_class() == 'Toplevel': return True return False #@+node:tom.20220518082700.148: *4* test_announce def test_announce(self): self.announce('testing the Announcer') #@-others #@+node:tom.20220518082717.1: ** __main__ if __name__ == '__main__': matplotlib.rcParams['xtick.direction'] = 'out' matplotlib.rcParams['ytick.direction'] = 'out' plotmgr = PlotManager() plotmgr.root.update_idletasks() plotmgr.announce('Using: %s' % (sys.executable)) plotmgr.fadeit() ds = Dataset([1,5,10,20], [1,25,100,400]) plotmgr.set_data(ds) plotmgr.plotmain() Tk.mainloop() #@-others #@-leo