def __init__(self, *args, **kwargs): self._app = Application.instance self.__topwin = kwargs.pop('topwin') Group.__init__(self, *args, **kwargs) frm = Frame(self) self.__center = ParamItem(frm) setMultiAttr(self.__center, labelText='center(deg)', entryText=0, checkFunc=self._app.checkInt, entryWidth=5, labelWidth=10) self.__center.pack(side=TOP) self._app.balloon.bind_widget( self.__center, balloonmsg='Specify the beam center here.') self.__width = ParamItem(frm) setMultiAttr(self.__width, labelText='width(deg)', entryText=20, checkFunc=self._app.checkInt, entryWidth=5, labelWidth=10) self.__width.pack(side=TOP) self._app.balloon.bind_widget( self.__width, balloonmsg='Specify the beam width here.') self.__uiImages = [] imageAddBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Add_Button.png')) self.__uiImages.append(imageAddBtn) btn = Button(frm, image=imageAddBtn, command=self.onAdd) btn.pack(side=LEFT) self._app.balloon.bind_widget( btn, balloonmsg='Add new beam to the ideal pattern.') imageDelBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Del_Button.png')) self.__uiImages.append(imageDelBtn) btn = Button(frm, image=imageDelBtn, command=self.onDel) btn.pack(side=LEFT) self._app.balloon.bind_widget( btn, balloonmsg='Remove the selected beam in the listbox.') imageClrBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Clear_Button.png')) self.__uiImages.append(imageClrBtn) btn = Button(frm, image=imageClrBtn, command=self.onClear) btn.pack(side=LEFT) self._app.balloon.bind_widget( btn, balloonmsg='Clear the listbox of the beam parameters.') imagePlotBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Plot_Button.png')) self.__uiImages.append(imagePlotBtn) btn = Button(frm, image=imagePlotBtn, command=self.onPlotIdealPattern) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Plot the ideal pattern.') frm.pack(side=LEFT, fill=Y) self.__paramlist = ScrolledList(self) self.__paramlist.list.config(height=4, width=10) self.__paramlist.pack(side=LEFT) self.name = 'Edit Ideal Pattern' self.optgrp = None
def __init__(self, *args, **kwargs): ''' nodeName: The name of this node. Usually set by ModelNode.__setattr__ automatically. figureMeta: Meta information of figure. The rest parameters are passed to PanedWindow.__init__. ''' nodeName = kwargs.pop('nodeName', '') # lock super(FigureBook, self).__init__(nodeName=nodeName) figureMeta = None if 'figureMeta' not in kwargs \ else kwargs.pop('figureMeta') kwargs['orient'] = HORIZONTAL panedWindow = PanedWindow(*args, **kwargs) panedWindow.config(sashwidth=4, sashrelief=GROOVE, bg='forestgreen') # figureTabsStyle = Style() # figureTabsStyle.configure('Figure.TNotebook', tabposition='sw') # figureTabs = Notebook(panedWindow, style='Figure.TNotebook') figureTabs = Notebook(panedWindow) self.figureTabs = figureTabs figureTabs.bind('<<NotebookTabChanged>>', self.onTabChange) self.lockAttribute('figureTabs') if figureMeta: self.makeFigures(figureMeta) self.lockElements() panedWindow.add(figureTabs, stretch='always') listPan = PanedWindow(panedWindow, orient=VERTICAL) listPan.config(sashwidth=4, sashrelief=GROOVE, bg='forestgreen') panedWindow.add(listPan, stretch='never') listFrm = Frame(listPan) listPan.add(listFrm, stretch='always') Label(listFrm, text='Curves', bg='#b5d6b0').pack(side=TOP, fill=X) self.__list = ScrolledList(listFrm, relief=GROOVE) self.__list.listConfig(width=20) self.__list.listClick = self.onListClick self.__list.pack(fill=BOTH, expand=YES) listFrm = Frame(listPan) listPan.add(listFrm, stretch='never') Label(listFrm, text='Indicators', bg='#b5d6b0').pack(side=TOP, fill=X) self.__indicatorListbox = ScrolledList(listFrm, relief=GROOVE) self.__indicatorListbox.listConfig(width=20) self.__indicatorListbox.pack(fill=BOTH, expand=YES) with self.attributeLock: setMultiAttr( self, panedWindow=panedWindow, gridGroupObserver=self.GridGroupObserver(self), axisGroupObserver=self.AxisGroupObserver(self), clearGroupObserver=self.ClearGroupObserver(self), labelGroupObserver=self.LabelGroupObserver(self), indicatorGroupObserver=self.IndicatorGroupObserver(self), dataFigureObserver=self.DataFigureObserver(self), dataPool=[])
class EditGroup(Group): def __init__(self, *args, **kwargs): self._app = Application.instance self.__topwin = kwargs.pop('topwin') Group.__init__(self, *args, **kwargs) frm = Frame(self) self.__center = ParamItem(frm) setMultiAttr(self.__center, labelText='center(deg)', entryText=0, checkFunc=self._app.checkInt, entryWidth=5, labelWidth=10) self.__center.pack(side=TOP) self._app.balloon.bind_widget( self.__center, balloonmsg='Specify the beam center here.') self.__width = ParamItem(frm) setMultiAttr(self.__width, labelText='width(deg)', entryText=20, checkFunc=self._app.checkInt, entryWidth=5, labelWidth=10) self.__width.pack(side=TOP) self._app.balloon.bind_widget( self.__width, balloonmsg='Specify the beam width here.') self.__uiImages = [] imageAddBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Add_Button.png')) self.__uiImages.append(imageAddBtn) btn = Button(frm, image=imageAddBtn, command=self.onAdd) btn.pack(side=LEFT) self._app.balloon.bind_widget( btn, balloonmsg='Add new beam to the ideal pattern.') imageDelBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Del_Button.png')) self.__uiImages.append(imageDelBtn) btn = Button(frm, image=imageDelBtn, command=self.onDel) btn.pack(side=LEFT) self._app.balloon.bind_widget( btn, balloonmsg='Remove the selected beam in the listbox.') imageClrBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Clear_Button.png')) self.__uiImages.append(imageClrBtn) btn = Button(frm, image=imageClrBtn, command=self.onClear) btn.pack(side=LEFT) self._app.balloon.bind_widget( btn, balloonmsg='Clear the listbox of the beam parameters.') imagePlotBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Plot_Button.png')) self.__uiImages.append(imagePlotBtn) btn = Button(frm, image=imagePlotBtn, command=self.onPlotIdealPattern) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Plot the ideal pattern.') frm.pack(side=LEFT, fill=Y) self.__paramlist = ScrolledList(self) self.__paramlist.list.config(height=4, width=10) self.__paramlist.pack(side=LEFT) self.name = 'Edit Ideal Pattern' self.optgrp = None def onAdd(self): self.__paramlist.list.insert( END, '{0}, {1}'.format(self.__center.getInt(), self.__width.getInt())) def onDel(self): self.__paramlist.list.delete(ANCHOR) def onClear(self): self.__paramlist.clear() def onPlotIdealPattern(self): printCode = True center, width = self.beamData self.__topwin.setIdealPattern(center, width) self.__topwin.plotIdealPattern() @property def beamData(self): beamParams = self.__paramlist.list.get(0, END) if not beamParams: self._app.printError('An error occurred!') self._app.printTip([{ 'type': 'text', 'content': '''This exception happens when the listbox of the beam parameters are empty. To make a valid ideal pattern, at least one beam should be specified. ''' }]) return center, width = zip( *[map(float, param.split(',')) for param in beamParams]) return center, width
class EditGroup(Group): def __init__(self, *args, **kwargs): self._app = Scripting.root_node self.__topwin = kwargs.pop('topwin') Group.__init__(self, *args, **kwargs) frm = Frame(self) self.__center = LabeledEntry(frm) set_attributes(self.__center, label_text = 'center(deg)', entry_text = 0, checker_function = self._app.check_int, entry_width = 5, label_width = 10 ) self.__center.pack(side=TOP) self._app.balloon.bind_widget(self.__center, balloonmsg='Specify the beam center here.') self.__width = LabeledEntry(frm) set_attributes(self.__width, label_text = 'width(deg)', entry_text = 20, checker_function = self._app.check_int, entry_width = 5, label_width = 10 ) self.__width.pack(side=TOP) self._app.balloon.bind_widget(self.__width, balloonmsg='Specify the beam width here.') self.__gui_images = [] imageAddBtn = ImageTk.PhotoImage( file=get_gui_image_path('Pattern_Add_Button.png') ) self.__gui_images.append(imageAddBtn) btn = Button(frm, image=imageAddBtn, command=self._on_add_button_click) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Add new beam to the ideal pattern.') imageDelBtn = ImageTk.PhotoImage( file=get_gui_image_path('Pattern_Del_Button.png') ) self.__gui_images.append(imageDelBtn) btn = Button(frm, image=imageDelBtn, command=self._on_delete_button_click) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Remove the selected beam in the listbox.') imageClrBtn = ImageTk.PhotoImage( file=get_gui_image_path('Pattern_Clear_Button.png') ) self.__gui_images.append(imageClrBtn) btn = Button(frm, image=imageClrBtn, command=self._on_clear_button_click) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Clear the listbox of the beam parameters.') imagePlotBtn = ImageTk.PhotoImage( file=get_gui_image_path('Pattern_Plot_Button.png') ) self.__gui_images.append(imagePlotBtn) btn = Button(frm, image=imagePlotBtn, command=self._on_plot_ideal_pattern) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Plot the ideal pattern.') frm.pack(side=LEFT, fill=Y) self.__paramlist = ScrolledList(self) self.__paramlist.list.config(height=4, width=10) self.__paramlist.pack(side=LEFT) self.name = 'Edit Ideal Pattern' self.optgrp = None def _on_add_button_click(self): self.__paramlist.list.insert(END, '{0}, {1}'.format(self.__center.get_int(), self.__width.get_int())) def _on_delete_button_click(self): self.__paramlist.list.delete(ANCHOR) def _on_clear_button_click(self): self.__paramlist.clear() def _on_plot_ideal_pattern(self): with code_printer: center, width = self.beam_data self.__topwin.set_ideal_pattern(center, width) self.__topwin.plot_ideal_pattern() @property def beam_data(self): beamParams = self.__paramlist.list.get(0, END) if not beamParams: self._app.print_error('An error occurred!') self._app.print_tip( [ { 'type':'text', 'content':'''This exception happens when the listbox of the beam parameters are empty. To make a valid ideal pattern, at least one beam should be specified. ''' } ] ) return center, width = zip(*[map(float, param.split(',')) for param in beamParams]) return center, width
class FigureBook(Observable, FigureList): '''FigureBook is a widget including multiple DataFigure objects and a Tkinter list widget. It is used to show the different aspects of a mathematical object. For example, FigureBook is used to show the envelope, phase, autocorrelation, and FTM of a vector. FigureBook supports Observable protocal. When some of its properties change, it will notify its observers, and its notifyObservers method will pass the following paramters to its observers: ----Grid Properties---- majorGrid: Bool, indicates that whether the major grid is on or off. minorGrid: Bool, indicates the minor grid. ----Axis Limits---- xLim: the lower and upper limits of the x axis. yLim: the lower and upper limits of the y axis. ----Tick---- majorXTick: the x axis' tick of the major grid. majorYTick: the y axis' ... minorXTick: the x axis' tick of the minor grid. minorYTick: the y axis' ...''' class GridGroupObserver(object): '''This class is used by FigureBook. FigureBook is a class supports observable protocal. Meanwhile, FigureBook can also observe other objects. This class, GridGroupObserver, can be used to observe an instance of the GridGroup class. ''' def __init__(self, figureBook): self.__figureBook = figureBook def update(self, majorGrid, minorGrid, props=None): printCode = True if not props: props = {'major': {}, 'minor': {}} currentFigure = self.__figureBook.currentFigure currentFigure.grid(majorGrid, which='major', **props['major']) currentFigure.grid(minorGrid, which='minor', **props['minor']) class AxisGroupObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, xLim, yLim, majorXTick, majorYTick, minorXTick, minorYTick, autoScale=False): printCode = True currentFigure = self.__figureBook.currentFigure if autoScale: currentFigure.autoScale() return lim = list(xLim) if currentFigure.isPolar: lim = list(deg2rad(lim)) majorXTick = deg2rad(majorXTick) if minorXTick is not None: minorXTick = deg2rad(minorXTick) lim.extend(yLim) currentFigure.axis(lim) for XY in ('X', 'Y'): for mm in ('major', 'minor'): currentFigure.setTick(mm + XY + 'Tick', locals()[mm + XY + 'Tick']) class ClearGroupObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, delType): printCode = True if delType == 'all': self.__figureBook.clear() elif delType == 'sel': self.__figureBook.deleteSelLines(idx=None) else: return class LabelGroupObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, labelType, labelString): nameMap = { 'title': 'setTitle', 'xlabel': 'setXLabel', 'ylabel': 'setYLabel' } printCode = True currentFigure = self.__figureBook.currentFigure getattr(currentFigure, nameMap[labelType])(labelString) class IndicatorGroupObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, meta): printCode = True if meta['type'] in ('axvspan', 'axhspan'): if meta['type'] == 'axvspan': theMin = meta['xmin'] theMax = meta['xmax'] else: theMin = meta['ymin'] theMax = meta['ymax'] props = meta['props'] # self.__figureBook.currentFigure.indicators.axvspan(xmin, xmax, **props) getattr(self.__figureBook.currentFigure.indicators, meta['type'])(theMin, theMax, **props) self.__figureBook.updateIndicatorList() class DataFigureObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, *args, **kwargs): self.__figureBook.notifyObservers(*args, **kwargs) def __init__(self, *args, **kwargs): ''' nodeName: The name of this node. Usually set by ModelNode.__setattr__ automatically. figureMeta: Meta information of figure. The rest parameters are passed to PanedWindow.__init__. ''' nodeName = kwargs.pop('nodeName', '') # lock super(FigureBook, self).__init__(nodeName=nodeName) figureMeta = None if 'figureMeta' not in kwargs \ else kwargs.pop('figureMeta') kwargs['orient'] = HORIZONTAL panedWindow = PanedWindow(*args, **kwargs) panedWindow.config(sashwidth=4, sashrelief=GROOVE, bg='forestgreen') # figureTabsStyle = Style() # figureTabsStyle.configure('Figure.TNotebook', tabposition='sw') # figureTabs = Notebook(panedWindow, style='Figure.TNotebook') figureTabs = Notebook(panedWindow) self.figureTabs = figureTabs figureTabs.bind('<<NotebookTabChanged>>', self.onTabChange) self.lockAttribute('figureTabs') if figureMeta: self.makeFigures(figureMeta) self.lockElements() panedWindow.add(figureTabs, stretch='always') listPan = PanedWindow(panedWindow, orient=VERTICAL) listPan.config(sashwidth=4, sashrelief=GROOVE, bg='forestgreen') panedWindow.add(listPan, stretch='never') listFrm = Frame(listPan) listPan.add(listFrm, stretch='always') Label(listFrm, text='Curves', bg='#b5d6b0').pack(side=TOP, fill=X) self.__list = ScrolledList(listFrm, relief=GROOVE) self.__list.listConfig(width=20) self.__list.listClick = self.onListClick self.__list.pack(fill=BOTH, expand=YES) listFrm = Frame(listPan) listPan.add(listFrm, stretch='never') Label(listFrm, text='Indicators', bg='#b5d6b0').pack(side=TOP, fill=X) self.__indicatorListbox = ScrolledList(listFrm, relief=GROOVE) self.__indicatorListbox.listConfig(width=20) self.__indicatorListbox.pack(fill=BOTH, expand=YES) with self.attributeLock: setMultiAttr( self, panedWindow=panedWindow, gridGroupObserver=self.GridGroupObserver(self), axisGroupObserver=self.AxisGroupObserver(self), clearGroupObserver=self.ClearGroupObserver(self), labelGroupObserver=self.LabelGroupObserver(self), indicatorGroupObserver=self.IndicatorGroupObserver(self), dataFigureObserver=self.DataFigureObserver(self), dataPool=[]) def append(self, val): val.addObserver(self.dataFigureObserver) return super(FigureBook, self).append(val) def notifyObservers(self, **kwargs): cf = self.currentFigure props = [ 'majorGrid', 'minorGrid', 'xLim', 'yLim', 'majorXTick', 'majorYTick', 'minorXTick', 'minorYTick' ] for p in props: if p not in kwargs: kwargs[p] = getattr(cf, p) if cf.isPolar: kwargs['xLim'] = rad2deg(kwargs['xLim']) kwargs['majorXTick'] = rad2deg(kwargs['majorXTick']) if kwargs['minorXTick'] is not None: kwargs['minorXTick'] = rad2deg(kwargs['minorXTick']) super(FigureBook, self).notifyObservers(**kwargs) def pack(self, *args, **kwargs): self.panedWindow.pack(*args, **kwargs) def makeFigures(self, figureMeta): for meta in figureMeta: frm = Frame(self.figureTabs) fig = DataFigure(frm, isPolar=meta['polar']) self.figureTabs.add(frm, text=meta['name']) self.append(fig) @property def currentFigure(self): return self[self.figureTabs.index(CURRENT)] @property def currentFigureIndex(self): return self.figureTabs.index(CURRENT) def plot(self, *args, **kwargs): try: curveName = kwargs.pop('curveName') except KeyError: curveName = 'curve' for fig in self: fig.plotFunction(*args, **kwargs) self.__list.insert(END, curveName) if 'color' in kwargs: color = colorMap[kwargs['color']] else: color = colorMap[self[0].lineObjects[-1][0].get_color()] self.__list.itemConfig(END, fg=color) self.notifyObservers() @Scripting.printable def clear(self): for fig in self: fig.clear() self.__list.clear() del self.dataPool[:] self.currentFigure.indicators.clear() self.updateIndicatorList() def onTabChange(self, event): self.notifyObservers() self.updateIndicatorList() def onListClick(self, index, label): index = int(index) for figure in self: for line in figure.lineObjects: pyplot.setp(line, linewidth=1) pyplot.setp(figure.lineObjects[index], linewidth=2) figure.update() @Scripting.printable def exportMatlabScript(self, filename): with open(filename, 'w') as file: for figure in self: print('%Generated by WaveSyn.', 'figure;', sep='\n', file=file) for line in figure.lineObjects: params = {} for name in ('xdata', 'ydata', 'color'): params[name] = pyplot.getp(line[0], name) params['func'] = 'polar' if figure.isPolar else 'plot' params['xdata'] = ','.join( (str(i) for i in params['xdata'])) params['ydata'] = ','.join( (str(i) for i in params['ydata'])) print("{func}([{xdata}], [{ydata}], '{color}');hold on". format(**params), file=file) # To do: Grid @Scripting.printable def deleteSelLines(self, idx=None): if idx is None: idx = self.__list.curSelection # idx is a tuple of strings. if len(idx) <= 0: return if len(idx) > 1: raise ValueError, 'Multi-selection is not supported.' idx = int(idx[0]) for fig in self: fig.lineObjects[idx][0].remove() del fig.lineObjects[idx] fig.update() self.__list.delete(idx) del self.dataPool[idx] def updateIndicatorList(self): iList = self.__indicatorListbox iList.delete(0, END) meta = self.currentFigure.indicators.meta for m in meta: if m['type'] == 'axvspan': xmin = m['xmin'] xmax = m['xmax'] iList.insert(END, '{0}|{1}/{2}'.format('axvspan', xmin, xmax)) elif m['type'] == 'axhspan': ymin = m['ymin'] ymax = m['ymax'] iList.insert(END, '{0}|{1}/{2}'.format('axhspan', ymin, ymax))
def __init__(self, *args, **kwargs): self._app = Scripting.root_node self.__topwin = kwargs.pop('topwin') Group.__init__(self, *args, **kwargs) frm = Frame(self) self.__center = LabeledEntry(frm) set_attributes(self.__center, label_text = 'center(deg)', entry_text = 0, checker_function = self._app.check_int, entry_width = 5, label_width = 10 ) self.__center.pack(side=TOP) self._app.balloon.bind_widget(self.__center, balloonmsg='Specify the beam center here.') self.__width = LabeledEntry(frm) set_attributes(self.__width, label_text = 'width(deg)', entry_text = 20, checker_function = self._app.check_int, entry_width = 5, label_width = 10 ) self.__width.pack(side=TOP) self._app.balloon.bind_widget(self.__width, balloonmsg='Specify the beam width here.') self.__gui_images = [] imageAddBtn = ImageTk.PhotoImage( file=get_gui_image_path('Pattern_Add_Button.png') ) self.__gui_images.append(imageAddBtn) btn = Button(frm, image=imageAddBtn, command=self._on_add_button_click) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Add new beam to the ideal pattern.') imageDelBtn = ImageTk.PhotoImage( file=get_gui_image_path('Pattern_Del_Button.png') ) self.__gui_images.append(imageDelBtn) btn = Button(frm, image=imageDelBtn, command=self._on_delete_button_click) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Remove the selected beam in the listbox.') imageClrBtn = ImageTk.PhotoImage( file=get_gui_image_path('Pattern_Clear_Button.png') ) self.__gui_images.append(imageClrBtn) btn = Button(frm, image=imageClrBtn, command=self._on_clear_button_click) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Clear the listbox of the beam parameters.') imagePlotBtn = ImageTk.PhotoImage( file=get_gui_image_path('Pattern_Plot_Button.png') ) self.__gui_images.append(imagePlotBtn) btn = Button(frm, image=imagePlotBtn, command=self._on_plot_ideal_pattern) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Plot the ideal pattern.') frm.pack(side=LEFT, fill=Y) self.__paramlist = ScrolledList(self) self.__paramlist.list.config(height=4, width=10) self.__paramlist.pack(side=LEFT) self.name = 'Edit Ideal Pattern' self.optgrp = None
def __init__(self, *args, **kwargs): """ nodeName: The name of this node. Usually set by ModelNode.__setattr__ automatically. figureMeta: Meta information of figure. The rest parameters are passed to PanedWindow.__init__. """ nodeName = kwargs.pop("nodeName", "") # lock super(FigureBook, self).__init__(nodeName=nodeName) figureMeta = None if "figureMeta" not in kwargs else kwargs.pop("figureMeta") kwargs["orient"] = HORIZONTAL panedWindow = PanedWindow(*args, **kwargs) panedWindow.config(sashwidth=4, sashrelief=GROOVE, bg="forestgreen") # figureTabsStyle = Style() # figureTabsStyle.configure('Figure.TNotebook', tabposition='sw') # figureTabs = Notebook(panedWindow, style='Figure.TNotebook') figureTabs = Notebook(panedWindow) self.figureTabs = figureTabs figureTabs.bind("<<NotebookTabChanged>>", self.onTabChange) self.lockAttribute("figureTabs") if figureMeta: self.makeFigures(figureMeta) self.lockElements() panedWindow.add(figureTabs, stretch="always") listPan = PanedWindow(panedWindow, orient=VERTICAL) listPan.config(sashwidth=4, sashrelief=GROOVE, bg="forestgreen") panedWindow.add(listPan, stretch="never") listFrm = Frame(listPan) listPan.add(listFrm, stretch="always") Label(listFrm, text="Curves", bg="#b5d6b0").pack(side=TOP, fill=X) self.__list = ScrolledList(listFrm, relief=GROOVE) self.__list.listConfig(width=20) self.__list.listClick = self.onListClick self.__list.pack(fill=BOTH, expand=YES) listFrm = Frame(listPan) listPan.add(listFrm, stretch="never") Label(listFrm, text="Indicators", bg="#b5d6b0").pack(side=TOP, fill=X) self.__indicatorListbox = ScrolledList(listFrm, relief=GROOVE) self.__indicatorListbox.listConfig(width=20) self.__indicatorListbox.pack(fill=BOTH, expand=YES) with self.attributeLock: setMultiAttr( self, panedWindow=panedWindow, gridGroupObserver=self.GridGroupObserver(self), axisGroupObserver=self.AxisGroupObserver(self), clearGroupObserver=self.ClearGroupObserver(self), labelGroupObserver=self.LabelGroupObserver(self), indicatorGroupObserver=self.IndicatorGroupObserver(self), dataFigureObserver=self.DataFigureObserver(self), dataPool=[], )
class FigureBook(Observable, FigureList): """FigureBook is a widget including multiple DataFigure objects and a Tkinter list widget. It is used to show the different aspects of a mathematical object. For example, FigureBook is used to show the envelope, phase, autocorrelation, and FTM of a vector. FigureBook supports Observable protocal. When some of its properties change, it will notify its observers, and its notifyObservers method will pass the following paramters to its observers: ----Grid Properties---- majorGrid: Bool, indicates that whether the major grid is on or off. minorGrid: Bool, indicates the minor grid. ----Axis Limits---- xLim: the lower and upper limits of the x axis. yLim: the lower and upper limits of the y axis. ----Tick---- majorXTick: the x axis' tick of the major grid. majorYTick: the y axis' ... minorXTick: the x axis' tick of the minor grid. minorYTick: the y axis' ...""" class GridGroupObserver(object): """This class is used by FigureBook. FigureBook is a class supports observable protocal. Meanwhile, FigureBook can also observe other objects. This class, GridGroupObserver, can be used to observe an instance of the GridGroup class. """ def __init__(self, figureBook): self.__figureBook = figureBook def update(self, majorGrid, minorGrid, props=None): printCode = True if not props: props = {"major": {}, "minor": {}} currentFigure = self.__figureBook.currentFigure currentFigure.grid(majorGrid, which="major", **props["major"]) currentFigure.grid(minorGrid, which="minor", **props["minor"]) class AxisGroupObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, xLim, yLim, majorXTick, majorYTick, minorXTick, minorYTick, autoScale=False): printCode = True currentFigure = self.__figureBook.currentFigure if autoScale: currentFigure.autoScale() return lim = list(xLim) if currentFigure.isPolar: lim = list(deg2rad(lim)) majorXTick = deg2rad(majorXTick) if minorXTick is not None: minorXTick = deg2rad(minorXTick) lim.extend(yLim) currentFigure.axis(lim) for XY in ("X", "Y"): for mm in ("major", "minor"): currentFigure.setTick(mm + XY + "Tick", locals()[mm + XY + "Tick"]) class ClearGroupObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, delType): printCode = True if delType == "all": self.__figureBook.clear() elif delType == "sel": self.__figureBook.deleteSelLines(idx=None) else: return class LabelGroupObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, labelType, labelString): nameMap = {"title": "setTitle", "xlabel": "setXLabel", "ylabel": "setYLabel"} printCode = True currentFigure = self.__figureBook.currentFigure getattr(currentFigure, nameMap[labelType])(labelString) class IndicatorGroupObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, meta): printCode = True if meta["type"] in ("axvspan", "axhspan"): if meta["type"] == "axvspan": theMin = meta["xmin"] theMax = meta["xmax"] else: theMin = meta["ymin"] theMax = meta["ymax"] props = meta["props"] # self.__figureBook.currentFigure.indicators.axvspan(xmin, xmax, **props) getattr(self.__figureBook.currentFigure.indicators, meta["type"])(theMin, theMax, **props) self.__figureBook.updateIndicatorList() class DataFigureObserver(object): def __init__(self, figureBook): self.__figureBook = figureBook def update(self, *args, **kwargs): self.__figureBook.notifyObservers(*args, **kwargs) def __init__(self, *args, **kwargs): """ nodeName: The name of this node. Usually set by ModelNode.__setattr__ automatically. figureMeta: Meta information of figure. The rest parameters are passed to PanedWindow.__init__. """ nodeName = kwargs.pop("nodeName", "") # lock super(FigureBook, self).__init__(nodeName=nodeName) figureMeta = None if "figureMeta" not in kwargs else kwargs.pop("figureMeta") kwargs["orient"] = HORIZONTAL panedWindow = PanedWindow(*args, **kwargs) panedWindow.config(sashwidth=4, sashrelief=GROOVE, bg="forestgreen") # figureTabsStyle = Style() # figureTabsStyle.configure('Figure.TNotebook', tabposition='sw') # figureTabs = Notebook(panedWindow, style='Figure.TNotebook') figureTabs = Notebook(panedWindow) self.figureTabs = figureTabs figureTabs.bind("<<NotebookTabChanged>>", self.onTabChange) self.lockAttribute("figureTabs") if figureMeta: self.makeFigures(figureMeta) self.lockElements() panedWindow.add(figureTabs, stretch="always") listPan = PanedWindow(panedWindow, orient=VERTICAL) listPan.config(sashwidth=4, sashrelief=GROOVE, bg="forestgreen") panedWindow.add(listPan, stretch="never") listFrm = Frame(listPan) listPan.add(listFrm, stretch="always") Label(listFrm, text="Curves", bg="#b5d6b0").pack(side=TOP, fill=X) self.__list = ScrolledList(listFrm, relief=GROOVE) self.__list.listConfig(width=20) self.__list.listClick = self.onListClick self.__list.pack(fill=BOTH, expand=YES) listFrm = Frame(listPan) listPan.add(listFrm, stretch="never") Label(listFrm, text="Indicators", bg="#b5d6b0").pack(side=TOP, fill=X) self.__indicatorListbox = ScrolledList(listFrm, relief=GROOVE) self.__indicatorListbox.listConfig(width=20) self.__indicatorListbox.pack(fill=BOTH, expand=YES) with self.attributeLock: setMultiAttr( self, panedWindow=panedWindow, gridGroupObserver=self.GridGroupObserver(self), axisGroupObserver=self.AxisGroupObserver(self), clearGroupObserver=self.ClearGroupObserver(self), labelGroupObserver=self.LabelGroupObserver(self), indicatorGroupObserver=self.IndicatorGroupObserver(self), dataFigureObserver=self.DataFigureObserver(self), dataPool=[], ) def append(self, val): val.addObserver(self.dataFigureObserver) return super(FigureBook, self).append(val) def notifyObservers(self, **kwargs): cf = self.currentFigure props = ["majorGrid", "minorGrid", "xLim", "yLim", "majorXTick", "majorYTick", "minorXTick", "minorYTick"] for p in props: if p not in kwargs: kwargs[p] = getattr(cf, p) if cf.isPolar: kwargs["xLim"] = rad2deg(kwargs["xLim"]) kwargs["majorXTick"] = rad2deg(kwargs["majorXTick"]) if kwargs["minorXTick"] is not None: kwargs["minorXTick"] = rad2deg(kwargs["minorXTick"]) super(FigureBook, self).notifyObservers(**kwargs) def pack(self, *args, **kwargs): self.panedWindow.pack(*args, **kwargs) def makeFigures(self, figureMeta): for meta in figureMeta: frm = Frame(self.figureTabs) fig = DataFigure(frm, isPolar=meta["polar"]) self.figureTabs.add(frm, text=meta["name"]) self.append(fig) @property def currentFigure(self): return self[self.figureTabs.index(CURRENT)] @property def currentFigureIndex(self): return self.figureTabs.index(CURRENT) def plot(self, *args, **kwargs): try: curveName = kwargs.pop("curveName") except KeyError: curveName = "curve" for fig in self: fig.plotFunction(*args, **kwargs) self.__list.insert(END, curveName) if "color" in kwargs: color = colorMap[kwargs["color"]] else: color = colorMap[self[0].lineObjects[-1][0].get_color()] self.__list.itemConfig(END, fg=color) self.notifyObservers() @Scripting.printable def clear(self): for fig in self: fig.clear() self.__list.clear() del self.dataPool[:] self.currentFigure.indicators.clear() self.updateIndicatorList() def onTabChange(self, event): self.notifyObservers() self.updateIndicatorList() def onListClick(self, index, label): index = int(index) for figure in self: for line in figure.lineObjects: pyplot.setp(line, linewidth=1) pyplot.setp(figure.lineObjects[index], linewidth=2) figure.update() @Scripting.printable def exportMatlabScript(self, filename): with open(filename, "w") as file: for figure in self: print("%Generated by WaveSyn.", "figure;", sep="\n", file=file) for line in figure.lineObjects: params = {} for name in ("xdata", "ydata", "color"): params[name] = pyplot.getp(line[0], name) params["func"] = "polar" if figure.isPolar else "plot" params["xdata"] = ",".join((str(i) for i in params["xdata"])) params["ydata"] = ",".join((str(i) for i in params["ydata"])) print("{func}([{xdata}], [{ydata}], '{color}');hold on".format(**params), file=file) # To do: Grid @Scripting.printable def deleteSelLines(self, idx=None): if idx is None: idx = self.__list.curSelection # idx is a tuple of strings. if len(idx) <= 0: return if len(idx) > 1: raise ValueError, "Multi-selection is not supported." idx = int(idx[0]) for fig in self: fig.lineObjects[idx][0].remove() del fig.lineObjects[idx] fig.update() self.__list.delete(idx) del self.dataPool[idx] def updateIndicatorList(self): iList = self.__indicatorListbox iList.delete(0, END) meta = self.currentFigure.indicators.meta for m in meta: if m["type"] == "axvspan": xmin = m["xmin"] xmax = m["xmax"] iList.insert(END, "{0}|{1}/{2}".format("axvspan", xmin, xmax)) elif m["type"] == "axhspan": ymin = m["ymin"] ymax = m["ymax"] iList.insert(END, "{0}|{1}/{2}".format("axhspan", ymin, ymax))
def __init__(self, *args, **kwargs): self._app = Application.instance self.__topwin = kwargs.pop('topwin') Group.__init__(self, *args, **kwargs) frm = Frame(self) self.__center = ParamItem(frm) setMultiAttr(self.__center, labelText = 'center(deg)', entryText = 0, checkFunc = self._app.checkInt, entryWidth = 5, labelWidth = 10 ) self.__center.pack(side=TOP) self._app.balloon.bind_widget(self.__center, balloonmsg='Specify the beam center here.') self.__width = ParamItem(frm) setMultiAttr(self.__width, labelText = 'width(deg)', entryText = 20, checkFunc = self._app.checkInt, entryWidth = 5, labelWidth = 10 ) self.__width.pack(side=TOP) self._app.balloon.bind_widget(self.__width, balloonmsg='Specify the beam width here.') self.__uiImages = [] imageAddBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Add_Button.png') ) self.__uiImages.append(imageAddBtn) btn = Button(frm, image=imageAddBtn, command=self.onAdd) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Add new beam to the ideal pattern.') imageDelBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Del_Button.png') ) self.__uiImages.append(imageDelBtn) btn = Button(frm, image=imageDelBtn, command=self.onDel) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Remove the selected beam in the listbox.') imageClrBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Clear_Button.png') ) self.__uiImages.append(imageClrBtn) btn = Button(frm, image=imageClrBtn, command=self.onClear) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Clear the listbox of the beam parameters.') imagePlotBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Plot_Button.png') ) self.__uiImages.append(imagePlotBtn) btn = Button(frm, image=imagePlotBtn, command=self.onPlotIdealPattern) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Plot the ideal pattern.') frm.pack(side=LEFT, fill=Y) self.__paramlist = ScrolledList(self) self.__paramlist.list.config(height=4, width=10) self.__paramlist.pack(side=LEFT) self.name = 'Edit Ideal Pattern' self.optgrp = None
class EditGroup(Group): def __init__(self, *args, **kwargs): self._app = Application.instance self.__topwin = kwargs.pop('topwin') Group.__init__(self, *args, **kwargs) frm = Frame(self) self.__center = ParamItem(frm) setMultiAttr(self.__center, labelText = 'center(deg)', entryText = 0, checkFunc = self._app.checkInt, entryWidth = 5, labelWidth = 10 ) self.__center.pack(side=TOP) self._app.balloon.bind_widget(self.__center, balloonmsg='Specify the beam center here.') self.__width = ParamItem(frm) setMultiAttr(self.__width, labelText = 'width(deg)', entryText = 20, checkFunc = self._app.checkInt, entryWidth = 5, labelWidth = 10 ) self.__width.pack(side=TOP) self._app.balloon.bind_widget(self.__width, balloonmsg='Specify the beam width here.') self.__uiImages = [] imageAddBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Add_Button.png') ) self.__uiImages.append(imageAddBtn) btn = Button(frm, image=imageAddBtn, command=self.onAdd) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Add new beam to the ideal pattern.') imageDelBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Del_Button.png') ) self.__uiImages.append(imageDelBtn) btn = Button(frm, image=imageDelBtn, command=self.onDel) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Remove the selected beam in the listbox.') imageClrBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Clear_Button.png') ) self.__uiImages.append(imageClrBtn) btn = Button(frm, image=imageClrBtn, command=self.onClear) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Clear the listbox of the beam parameters.') imagePlotBtn = ImageTk.PhotoImage( file=uiImagePath('Pattern_Plot_Button.png') ) self.__uiImages.append(imagePlotBtn) btn = Button(frm, image=imagePlotBtn, command=self.onPlotIdealPattern) btn.pack(side=LEFT) self._app.balloon.bind_widget(btn, balloonmsg='Plot the ideal pattern.') frm.pack(side=LEFT, fill=Y) self.__paramlist = ScrolledList(self) self.__paramlist.list.config(height=4, width=10) self.__paramlist.pack(side=LEFT) self.name = 'Edit Ideal Pattern' self.optgrp = None def onAdd(self): self.__paramlist.list.insert(END, '{0}, {1}'.format(self.__center.getInt(), self.__width.getInt())) def onDel(self): self.__paramlist.list.delete(ANCHOR) def onClear(self): self.__paramlist.clear() def onPlotIdealPattern(self): printCode = True center, width = self.beamData self.__topwin.setIdealPattern(center, width) self.__topwin.plotIdealPattern() @property def beamData(self): beamParams = self.__paramlist.list.get(0, END) if not beamParams: self._app.printError('An error occurred!') self._app.printTip( [ { 'type':'text', 'content':'''This exception happens when the listbox of the beam parameters are empty. To make a valid ideal pattern, at least one beam should be specified. ''' } ] ) return center, width = zip(*[map(float, param.split(',')) for param in beamParams]) return center, width