Exemplo n.º 1
0
class LeftGraphBottom(wx.Panel):
    def __init__(self, parent, statusbar):
        wx.Panel.__init__(self, parent)
        self.statusbar = statusbar
        """
        An polygon editor.
        Key-bindings
          't' toggle vertex markers on and off.  When vertex markers are on,
              you can move them, delete them
          'd' delete the vertex under point
          'i' insert a vertex at point.  You must be within epsilon of the
              line connecting two existing vertices
        """
        self.fig = Figure((4.0, 3.0))
        self.canvas = FigCanvas(self, -1, self.fig)
        self.ax = self.fig.add_subplot(111)
        """ subplots_adjust(bottom=0.14): permet d'ajuster la taille du canevas
        en prenant en compte la legende
        sinon la legende est rognee"""
        self.fig.subplots_adjust(bottom=0.20)
        self.ax.set_ylabel("DW", fontdict=font)
        self.ax.set_xlabel("Depth ($\AA$)", fontdict=font)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Hide()
        self.fig.patch.set_facecolor(colorBackgroundGraph)

        self._ind = None  # the active vert
        self.poly = []
        self.line = []
        self.showverts = True
        self.epsilon = 5  # max pixel distance to count as a vertex hit
        self.new_coord = {'indice': 0, 'x': 0, 'y': 0}
        self.modelpv = False

        xs = [-1]
        ys = [-1]
        poly = Polygon(list(zip(xs, ys)),
                       ls='solid',
                       fill=False,
                       closed=False,
                       animated=True)
        self.ax.set_xlim([0, 1])
        self.ax.set_ylim([0, 1])
        self.c_dw = ""
        self.l_dw = ""

        self.canvas.mpl_connect('draw_event', self.draw_callback)
        self.canvas.mpl_connect('button_press_event',
                                self.button_press_callback)
        self.canvas.mpl_connect('button_release_event',
                                self.button_release_callback)
        self.canvas.mpl_connect('motion_notify_event',
                                self.motion_notify_callback)
        self.canvas.mpl_connect('scroll_event', self.scroll_callback)
        self.canvas.mpl_connect('motion_notify_event',
                                self.on_update_coordinate)

        mastersizer = wx.BoxSizer(wx.VERTICAL)
        mastersizer.Add(self.canvas, 1, wx.ALL | wx.EXPAND)
        mastersizer.Add(self.toolbar, 0, wx.ALL)

        pub.subscribe(self.draw_c, pubsub_draw_graph)
        pub.subscribe(self.OnDrawGraph, pubsub_Draw_DW)
        pub.subscribe(self.scale_manual, pubsub_Update_Scale_DW)
        pub.subscribe(self.on_color, pubsub_Graph_change_color_style)

        self.on_color()
        self.draw_c(poly, xs, ys)

        self.SetSizer(mastersizer)
        self.Fit()

    def on_color(self):
        a = P4Rm()
        self.c_dw = a.DefaultDict['c_dw']
        self.l_dw = a.DefaultDict['l_dw']
        self.c_bkg = a.DefaultDict['c_graph_background']

    def OnDrawGraph(self, b=None):
        a = P4Rm()
        self.modelpv = a.modelPv
        self.ax.clear()
        if a.AllDataDict['damaged_depth'] == 0:
            self.ax.text(0.5,
                         0.5,
                         "No Damage",
                         size=30,
                         rotation=0.,
                         ha="center",
                         va="center",
                         bbox=dict(
                             boxstyle="round",
                             ec='red',
                             fc=self.c_dw,
                         ))
            x_dwp = [-1]
            y_dwp = [-1]
            xs = [-1]
            ys = [-1]
            self.ax.set_xticklabels([])
            self.ax.set_yticklabels([])
            self.ax.set_xlim([0, 1])
            self.ax.set_ylim([0, 1])
        else:
            if b != 2:
                x_dwp = a.ParamDict['x_dwp']
                y_dwp = a.ParamDict['DW_shifted']
                xs = deepcopy(a.ParamDict['depth'])
                ys = deepcopy(a.ParamDict['DW_i'])
                P4Rm.DragDrop_DW_x = x_dwp
                P4Rm.DragDrop_DW_y = y_dwp
                ymin = min(ys) - min(ys) * 10 / 100
                ymax = max(ys) + max(ys) * 10 / 100
                self.ax.set_ylim([ymin, ymax])
                if a.ParamDict['x_dwp'] != "":
                    self.ax.set_xlim(
                        [a.ParamDict['depth'][-1], a.ParamDict['depth'][0]])
            elif b == 2:
                x_dwp = [-1]
                y_dwp = [-1]
                xs = [-1]
                ys = [-1]
                self.ax.set_xlim([0, 1])
                self.ax.set_ylim([0, 1])
        poly = Polygon(list(zip(x_dwp, y_dwp)),
                       lw=0,
                       ls='solid',
                       color=self.c_dw,
                       fill=False,
                       closed=False,
                       animated=True)
        if self.modelpv is True:
            P4Rm.ParamDict['dwp_pv_backup'] = a.ParamDict['dwp']
        self.draw_c(poly, xs, ys)

    def draw_c(self, data, x, y):
        self.ax.plot(x, y, color=self.c_dw, lw=2., ls='solid')
        self.ax.set_ylabel("DW", fontdict=font)
        self.ax.set_xlabel("Depth ($\AA$)", fontdict=font)
        if LooseVersion(matplotlib_vers) < LooseVersion("2.0.0"):
            self.ax.set_axis_bgcolor(self.c_bkg)
        else:
            self.ax.set_facecolor(self.c_bkg)
        self.poly = data
        xs, ys = zip(*self.poly.xy)
        self.line = Line2D(xs,
                           ys,
                           lw=0,
                           ls='solid',
                           color=self.c_dw,
                           marker='.',
                           ms=32,
                           markerfacecolor=self.c_dw,
                           markeredgecolor='k',
                           mew=1.0)
        self.ax.add_line(self.line)
        self.ax.add_patch(self.poly)
        self.canvas.SetCursor(Cursor(wx.CURSOR_HAND))
        self.canvas.draw()

    def draw_callback(self, event):
        self.background = self.canvas.copy_from_bbox(self.ax.bbox)
        self.ax.draw_artist(self.poly)
        self.ax.draw_artist(self.line)
        self.canvas.blit(self.ax.bbox)

    def get_ind_under_point(self, event):
        'get the index of the vertex under point if within epsilon tolerance'

        # display coords
        xy = np.asarray(self.poly.xy)
        xyt = self.poly.get_transform().transform(xy)
        xt, yt = xyt[:, 0], xyt[:, 1]
        d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2)
        indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
        ind = indseq[0]

        if d[ind] >= self.epsilon:
            ind = None
        return ind

    def button_press_callback(self, event):
        'whenever a mouse button is pressed'
        a = P4Rm()
        val = a.xrd_graph_loaded
        if self.canvas.HasCapture():
            self.canvas.ReleaseMouse()
            if not self.showverts:
                return
            if event.inaxes is None:
                return
            if event.button != 1:
                return
            if val == 1:
                self._ind = self.get_ind_under_point(event)
                self.new_coord['indice'] = self._ind

    def button_release_callback(self, event):
        'whenever a mouse button is released'
        a = P4Rm()
        val = a.xrd_graph_loaded
        if self.canvas.HasCapture():
            self.canvas.ReleaseMouse()
        else:
            if not self.showverts:
                return
            if event.button != 1:
                return
            if self.new_coord['indice'] is not None and val == 1:
                a = P4Rm()
                temp_1 = self.new_coord['y']
                temp_2 = self.new_coord['x']
                P4Rm.DragDrop_DW_y[self.new_coord['indice']] = temp_1
                P4Rm.DragDrop_DW_x[self.new_coord['indice']] = temp_2
                if a.AllDataDict['model'] == 0:
                    temp = self.new_coord['y']
                    P4Rm.DragDrop_DW_y[self.new_coord['indice']] = temp
                    temp = [
                        dw * scale for dw, scale in zip(
                            a.DragDrop_DW_y, a.ParamDict['scale_dw'])
                    ]
                    temp = [float(format(value, '.8f')) for value in temp]
                    temp2 = np.concatenate([temp, [a.ParamDict['dw_out']]])
                    P4Rm.ParamDict['dwp'] = deepcopy(temp2)
                    P4Rm.ParamDictbackup['dwp'] = deepcopy(temp2)
                elif a.AllDataDict['model'] == 1:
                    temp = self.new_coord['y']
                    P4Rm.DragDrop_DW_y[self.new_coord['indice']] = temp
                    temp = [
                        dw * scale for dw, scale in zip(
                            a.DragDrop_DW_y, a.ParamDict['scale_dw'])
                    ]
                    temp = [float(format(value, '.8f')) for value in temp]
                    temp2 = np.concatenate([[a.ParamDict['dw_out'][0]], temp,
                                            [a.ParamDict['dw_out'][1]]])
                    P4Rm.ParamDict['dwp'] = deepcopy(temp2)
                    P4Rm.ParamDictbackup['dwp'] = deepcopy(temp2)
                elif a.AllDataDict['model'] == 2:
                    t_temp = a.ParamDict['depth'] + a.ParamDict['z']
                    t = t_temp[0]
                    dwp_temp = range(7)
                    dwp_temp[0] = a.DragDrop_DW_y[0]
                    dwp_temp[1] = 1 - a.DragDrop_DW_x[0] / t
                    dwp_temp[2] = 2 * (-1 + a.ParamDict['dwp'][1] +
                                       a.DragDrop_DW_x[1] / t)
                    dwp_temp[3] = 2 * (1 - a.ParamDict['dwp'][1] -
                                       1 * a.DragDrop_DW_x[2] / t)
                    dwp_temp[4] = a.ParamDict['dwp'][4]
                    dwp_temp[5] = a.ParamDict['dwp'][5]
                    dwp_temp[6] = a.DragDrop_DW_y[3]
                    P4Rm.ParamDict['dwp'] = deepcopy(dwp_temp)
                    P4Rm.ParamDictbackup['dwp'] = deepcopy(dwp_temp)
                    P4Rm.ParamDict['dwp_pv'] = deepcopy(dwp_temp)
                pub.sendMessage(pubsub_Update_Fit_Live)
            self._ind = None

    def scroll_callback(self, event):
        if not event.inaxes:
            return
        a = P4Rm()
        if event.key == 'u' and event.button == 'up':
            temp = a.ParamDict['DW_multiplication'] + 0.01
            P4Rm.ParamDict['DW_multiplication'] = temp
        elif event.key == 'u' and event.button == 'down':
            temp = a.ParamDict['DW_multiplication'] - 0.01
            P4Rm.ParamDict['DW_multiplication'] = temp
        P4Rm.ParamDict['dwp'] = multiply(a.ParamDictbackup['dwp'],
                                         a.ParamDict['DW_multiplication'])
        pub.sendMessage(pubsub_Re_Read_field_paramters_panel, event=event)

    def scale_manual(self, event, val=None):
        a = P4Rm()
        if val is not None:
            P4Rm.ParamDict['DW_multiplication'] = val
        P4Rm.ParamDict['dwp'] = multiply(a.ParamDict['dwp'],
                                         a.ParamDict['DW_multiplication'])
        pub.sendMessage(pubsub_Re_Read_field_paramters_panel, event=event)

    def motion_notify_callback(self, event):
        'on mouse movement'
        a = P4Rm()
        if a.AllDataDict['damaged_depth'] == 0:
            return
        if not self.showverts:
            return
        if self._ind is None:
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        if self.modelpv is True:
            if self._ind == 0:
                y = event.ydata
                x = event.xdata
            elif self._ind == 1 or self._ind == 2:
                y = a.DragDrop_DW_y[self.new_coord['indice']]
                x = event.xdata
            else:
                x = a.DragDrop_DW_x[self.new_coord['indice']]
                y = event.ydata
        else:
            y = event.ydata
            x = a.DragDrop_DW_x[self.new_coord['indice']]

        self.new_coord['x'] = x
        self.new_coord['y'] = y
        self.poly.xy[self._ind] = x, y
        self.line.set_data(zip(*self.poly.xy))

        self.canvas.restore_region(self.background)
        self.ax.draw_artist(self.poly)
        self.ax.draw_artist(self.line)
        self.canvas.blit(self.ax.bbox)

    def on_update_coordinate(self, event):
        if event.inaxes is None:
            self.statusbar.SetStatusText(u"", 1)
            self.statusbar.SetStatusText(u"", 2)
        else:
            a = P4Rm()
            if not a.AllDataDict['damaged_depth'] == 0:
                x, y = event.xdata, event.ydata
                xfloat = round(float(x), 2)
                yfloat = round(float(y), 2)
                self.statusbar.SetStatusText(u"x = " + str(xfloat), 1)
                self.statusbar.SetStatusText(u"y = " + str(yfloat), 2)
                xy = np.asarray(self.poly.xy)
                xyt = self.poly.get_transform().transform(xy)
                xt, yt = xyt[:, 0], xyt[:, 1]
                d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2)
                indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
                ind = indseq[0]

                if d[ind] >= self.epsilon:
                    self.canvas.SetCursor(Cursor(wx.CURSOR_ARROW))
                elif d[ind] <= self.epsilon:
                    self.canvas.SetCursor(Cursor(wx.CURSOR_HAND))
class GenericPlotItemPanel(wx.Panel):
    """ plot on a PlotPanel one curve """
    def __init__(self,
                 parent,
                 value,
                 pression,
                 theName,
                 liste_item=None,
                 kind="GASES",
                 xlegend="ppmv",
                 edit=False,
                 layerstyle=False,
                 layer=None,
                 yInPressions=True,
                 tskin=None,
                 tickSize=10):

        self.theName = theName
        self.theParent = parent
        self.xlegend = xlegend
        self.edit = edit
        self.kind = kind
        self.yInPressions = yInPressions
        self.layer = layer
        self.layerstyle = layerstyle
        self.tickSize = tickSize
        self.pression = pression
        self.value = value
        self.myLayeritem = None
        self.tskin = []
        self.ytskin = []
        if tskin:
            self.tskin.append(tskin)

        wx.Panel.__init__(self, parent, style=wx.BORDER_SIMPLE)

        # define object for matplotlib
        self.fig = Figure()
        self.canvas = FigureCanvas(self, -1, self.fig)
        self.canvas.mpl_connect('motion_notify_event', self.onMouseMotion)

        self.text = wx.StaticText(self, -1, label="")
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.GROW, 1)
        self.tlb = ToolBar(self.canvas)
        self.sizer.Add(self.tlb, 0, wx.GROW)
        self.tlb.Realize()
        self.SetSizer(self.sizer)

        self.text = wx.StaticText(self, -1, label="")
        self.sizer.Add(self.text)
        self.Fit()

        self.onInsert = True
        self.myCurves = []
        self.OnPlot()
        self.valueHistory = []
        self.valueHistoryRedo = []

    def onResize(self, event):
        print "event resize", str(event)

    def onMouseMotion(self, event):
        """ set text when moving mousse """

        if event.inaxes:

            xdata = event.xdata
            ydata = event.ydata
            xstr = "%0.4g" % xdata
            ystr = "%0.4g" % ydata

            value = str(self.axes.get_ylabel()) + "=" + ystr + \
                "  " + str(self.axes.get_xlabel()) + "=" + xstr

            self.text.SetLabel(value)

    def OnPlot(self):
        """ effectively perform the graphics """
        self.SetTickSize(self.tickSize)

        self.fig.clear()
        self.axes = self.fig.add_subplot(1, 1, 1)

        self.x = self.value[::1]

        if self.yInPressions:
            self.axes.set_yscale("log")
            self.axes.set_yticks(
                (0.00005, 0.0001, 0.0002, 0.0005, 0.001, 0.002, 0.005, 0.01,
                 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 25, 50, 100, 200, 300,
                 500, 1000))
            label = ('5e-5', '1e-4', '2e-4', '5e-4', '1e-3', '2e-3', '5e-3',
                     '0.01', '0.02', '0.05', '0.1', '0.2', '0.5', '1', '2',
                     '5', '10', '25', '50', '100', '200', '300', '500', '1000')
            self.axes.set_yticklabels(label)
            self.axes.set_ylabel('pressure (hPa)')
            self.axes.set_ylim((self.pression[-1] + 150, self.pression[0]))
        else:
            self.axes.set_ylim(self.value.shape[0] + 2, 1)
            self.axes.set_ylabel('level')

        if self.kind == "GASES":
            if self.yInPressions:
                self.y = self.pression[::1]
            else:
                self.y = numpy.arange(1, self.value.shape[0] + 1, 1)
        else:
            if self.yInPressions:
                self.y = self.layer[::1]
            else:
                self.y = numpy.arange(1.5, self.value.shape[0], 1)

        if not self.layerstyle:
            self.data = Data(self.x,
                             self.y,
                             theName=self.theName,
                             theColor=itemColor[self.theName],
                             theMarker=itemMarker[self.theName])
        else:
            if self.yInPressions:
                self.myLayeritem = layeritem.Layeritem(
                    layeritem=self.x, pression=self.pression[::1])
            else:
                self.myLayeritem = layeritem.Layeritem(
                    layeritem=self.x,
                    pression=numpy.arange(1, self.value.shape[0] + 1, 1))
            (self.xlayeritem,
             self.ylayeritem) = (self.myLayeritem.computeLayerLine(
                 layers=self.y))
            self.data = Data(self.xlayeritem,
                             self.ylayeritem,
                             theName=self.theName,
                             theColor=itemColor[self.theName],
                             theMarker=itemMarker[self.theName])

        self.axes.set_xlabel(self.xlegend)
        self.SetXlimits(self.theName)
        self.axes.grid(True, axis='both')

        self.myChannelList = []
        self.myChannelList.append(self.data)

        if self.theName == "T":
            if len(self.tskin) > 0:
                if self.yInPressions:
                    self.ytskin.append(self.pression[-1] + 50)
                else:
                    self.ytskin.append(self.value.shape[0] + 1)
                datatskin = Data(self.tskin,
                                 self.ytskin,
                                 theName='TSKIN',
                                 theColor="red",
                                 theMarker="*")
                self.myChannelList.append(datatskin)

        if wx.Platform == '__WXMAC__':
            self.Update()

    def SetTickSize(self, size):
        matplotlib.rc('xtick', labelsize=size)
        matplotlib.rc('ytick', labelsize=size)

    def ConnectCanvasEVT_POINT(self, methode):
        self.cid = self.fig.canvas.mpl_connect("button_press_event", methode)

    def DisconnectCanvasEVT_POINT(self):
        self.fig.canvas.mpl_disconnect(self.cid)

    def SetXlimits(self, theName=None, xmin=None, xmax=None):
        """ set x limits """
        if xmin is not None and xmax is not None:
            self.axes.set_xlim((xmin, xmax))
        else:
            if axesDef[self.theName]["xlimits"] is not None:
                self.axes.set_xlim(axesDef[self.theName]["xlimits"])
            self.axes.set_xscale(axesDef[self.theName]["xscale"])

    def Update(self):
        """ erase the curve if necessary and redraw """
        if len(self.myCurves) == 1:
            if len(self.axes.lines) == 1:
                self.axes.lines.remove(self.axes.lines[0])
            self.myCurves.pop()

        for data in self.myChannelList:
            c = self.axes.plot(data.x,
                               data.y,
                               color=data.color,
                               marker=data.marker)
            self.myCurves.append(c)
        self.fig.canvas.draw_idle()

    def UpdateData(self, dataX):
        self.x = dataX
        self.data.setChanged(True)
        if not self.layerstyle:
            self.data.myUpdate(self.x, self.y)
        else:
            (self.xlayeritem,
             self.ylayeritem) = (self.myLayeritem.computeLayerLine(
                 layeritem=dataX))
            self.data.myUpdate(self.xlayeritem, self.ylayeritem)
        self.Update()

    def OnRedo(self):
        if self.valueHistoryRedo != []:
            if not self.layerstyle:
                X = numpy.zeros(self.x.shape[0]) + self.x
                self.valueHistory.append(X)
                X = self.valueHistoryRedo.pop()
                self.x = numpy.zeros(X.shape[0]) + X
                self.data.myUpdate(self.x, self.y)
            else:
                X = numpy.zeros(self.xlayeritem.shape[0]) + self.xlayeritem
                self.valueHistory.append(X)
                X = self.valueHistoryRedo.pop()
                self.xlayeritem = numpy.zeros(X.shape[0]) + X
                self.x = self.myLayeritem.getLayeritem(self.xlayeritem)
                self.myLayeritem.update(self.xlayeritem, self.ylayeritem)
                self.data.myUpdate(self.xlayeritem, self.ylayeritem)
            self.Update()

    def OnUndo(self):

        if self.valueHistory != []:
            if not self.layerstyle:
                X = numpy.zeros(self.x.shape[0]) + self.x
                self.valueHistoryRedo.append(X)
                X = self.valueHistory.pop()
                self.x = numpy.zeros(X.shape[0]) + X
                self.data.myUpdate(self.x, self.y)
            else:
                X = numpy.zeros(self.xlayeritem.shape[0]) + self.xlayeritem
                self.valueHistoryRedo.append(X)
                X = self.valueHistory.pop()
                self.xlayeritem = numpy.zeros(X.shape[0]) + X
                self.x = self.myLayeritem.getLayeritem(self.xlayeritem)
                self.data.myUpdate(self.xlayeritem, self.ylayeritem)

            self.Update()

    def OnPoint(self, e):
        """ OnPoint Methods """

        if (e.button == 1) or (e.dblclick):
            if self.canvas.HasCapture():
                self.canvas.ReleaseMouse()
            return (False)
        if e.xdata is None or e.ydata is None:
            if self.canvas.HasCapture():
                self.canvas.ReleaseMouse()
            if self.HasCapture():
                self.ReleaseMouse()
            return False
        if (e.ydata < self.y.min() or e.ydata > self.y.max()):
            if self.canvas.HasCapture():
                self.canvas.ReleaseMouse()
            return (False)
        # self.tlb.release_zoom(e)

        if not self.layerstyle:
            y = numpy.zeros(self.x.shape[0]) + self.x
            self.valueHistory.append(y)
            mini = 1000
            for index in range(self.y.shape[0]):
                dist = abs(self.y[index] - e.ydata)
                if dist < mini:
                    imin = index
                    mini = dist
            if self.kind != "GASES" and not self.onInsert:
                self.x[imin] = 0
            else:
                self.x[imin] = e.xdata
            self.data.setChanged(True)
            self.data.myUpdate(self.x, self.y)
            self.Update()

        else:

            y = numpy.zeros(self.xlayeritem.shape[0]) + self.xlayeritem
            self.valueHistory.append(y)
            mini = 1000
            for index in range(self.ylayeritem.shape[0]):
                dist = self.ylayeritem[index] - e.ydata
                if dist < mini and dist > 0:
                    imin = index
                    mini = dist

            if not self.onInsert:
                self.xlayeritem[imin] = 0
                # we have 2 points to move and its depends if imin is odd
                if imin % 2 != 0:
                    if imin != self.xlayeritem.shape[0]:
                        self.xlayeritem[imin + 1] = 0
                else:
                    if imin != 0:
                        self.xlayeritem[imin - 1] = 0
            else:
                self.xlayeritem[imin] = e.xdata
                # we have 2 points to move and its depends if imini is odd
                if imin % 2 != 0:
                    if imin != self.xlayeritem.shape[0]:
                        self.xlayeritem[imin + 1] = e.xdata
                else:
                    if imin != 0:
                        self.xlayeritem[imin - 1] = e.xdata

            self.data.setChanged(True)
            self.data.myUpdate(self.xlayeritem, self.ylayeritem)
            self.x = self.myLayeritem.getLayeritem(self.xlayeritem)
            self.Update()

        if self.canvas.HasCapture():
            self.canvas.ReleaseMouse()
        if self.HasCapture():
            self.ReleaseMouse()
        return True

    def GetItem(self):
        """ get value from curve (=data) and return a profile for the item """

        myX = self.data.getX()
        if self.layerstyle:
            layerX = self.myLayeritem.getLayeritem(myX)
            return layerX
        else:
            return myX
Exemplo n.º 3
0
class PlotPanel(wx.Panel):
    def __init__(self, parent, statusbar):
        wx.Panel.__init__(self, parent, -1, size=(50, 50))
        self.statusbar = statusbar

        self.plot_handl = None
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(self.sizer)

        self.figure = Figure(None, dpi=300)
        self.ax = self.figure.add_axes([0, 0, 1, 1],
                                       frameon=False,
                                       projection=ccrs.PlateCarree())
        self.canvas = PlotFigureCanvas(self, -1, self.figure)
        self.toolbar = PlotToolbar(self.canvas, self)

        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.sizer.Add(self.toolbar, 0, wx.EXPAND)

        # Clear artifacts
        self.clear_artifacts()

        # Bind events
        self.figure.canvas.mpl_connect('button_release_event', self.onclick)
        self.ax.callbacks.connect('xlim_changed', self.on_xlims_change)
        self.ax.callbacks.connect('ylim_changed', self.on_ylims_change)

        # setup empty list of meridians and parallels
        self.meridians_w = {}
        self.meridians_b = {}
        self.parallels_w = {}
        self.parallels_b = {}

        self.labels = []

        self.nlons = NUM_GRID_LINES
        self.nlats = NUM_GRID_LINES
        self.lons = []
        self.lats = []
        self.latll = -90
        self.latur = 90
        self.lonll = -180
        self.lonur = 180

        self.motion_display_font = FontProperties()
        self.motion_display_font.set_size(MOTION_DISPLAY_FONT_SIZE)
        self.motion_display_font.set_family('monospace')

        self.grid_label_font = FontProperties()
        self.grid_label_font.set_size(GRID_LABEL_FONT_SIZE)
        self.grid_label_font.set_family('monospace')

        self.map = None
        self.motion_display = None

        self._rc_zoomed = False

        self.cursor_coordinates_enabled = True
        self.coordinate_grid_enabled = True
        self.grid_labels_enabled = True

    def set_cursor_coordinates_enabled(self, enabled):
        self.cursor_coordinates_enabled = enabled

        # apply immediately
        if not enabled:
            if self.motion_display:
                self.motion_display.remove()
                self.figure.canvas.draw()
            self.motion_display = None

    def set_coordinate_grid_enabled(self, enabled):
        self.coordinate_grid_enabled = enabled

    def set_grid_labels_enabled(self, enabled):
        self.grid_labels_enabled = enabled

    def on_xlims_change(axes):
        pass

    def on_ylims_change(axes):
        pass

    def on_mouse_move(self, event):
        if not self.map:
            return
        lon, lat = self.map(event.xdata, event.ydata, inverse=True)
        lonll = max(self.lonll, self.map.llcrnrlon)
        lonur = min(self.lonur, self.map.urcrnrlon)
        latll = max(self.latll, self.map.llcrnrlat)
        latur = min(self.latur, self.map.urcrnrlat)
        if lat >= latll and lat <= latur and \
                lon >= lonll and lon <= lonur and \
                self.map.projection == 'cyl' and \
                self.cursor_coordinates_enabled:
            if self.motion_display is None:
                # setup coordinate display for mouse motion
                self.motion_display = self.ax.annotate(
                    s="(NaN, NaN)",
                    xy=(0.995, 0.995),
                    xycoords='axes fraction',
                    fontproperties=self.motion_display_font,
                    verticalalignment='top',
                    horizontalalignment='right',
                    bbox=dict(facecolor='white', alpha=1.0, pad=0.2, lw=0.2))

            display_lon = self.wrap_lon_at_day_boundary(lon)
            self.motion_display.set_text('(%+10.5f,%+10.5f)' %
                                         (display_lon, lat))
            self.ax.draw_artist(self.motion_display)
            self.canvas.blit(self.motion_display.get_window_extent())
        else:
            if self.motion_display:
                self.motion_display.remove()
                self.figure.canvas.draw()
            self.motion_display = None

    def wrap_lon_at_day_boundary(self, lon):
        wrapped_lon = lon
        while wrapped_lon < -180:
            wrapped_lon += 360.0
        while wrapped_lon > 180:
            wrapped_lon -= 360.0
        return wrapped_lon

    def do_dynamic_update(self):
        if not self.map:
            return

        # update meridians
        self._plot_meridians_and_parallels()
        self.figure.canvas.draw()

    def set_right_click_zoomed(self):
        self._rc_zoomed = True

    def onclick(self, event):
        if event is None or event.button != 3 or not event.inaxes:
            return
        if self._rc_zoomed:
            self._rc_zoomed = False
            return
        self.click_event = event
        menu = wx.Menu()
        menu.Append(wx.ID_ANY, "Add Point Here",
                    "Adds a point where the mouse was clicked on the map",
                    wx.ITEM_NORMAL)
        self.Bind(wx.EVT_MENU, self.callback, id=wx.ID_ANY)
        if self.canvas.HasCapture():
            self.canvas.ReleaseMouse()
        wx.CallAfter(self.PopupMenu, menu)

    def callback(self, event):
        self.map.plot(self.click_event.xdata,
                      self.click_event.ydata,
                      'r.',
                      markersize=1.)
        self.figure.canvas.draw()

    def clear_artifacts(self):
        for artifact in ['artists', 'lines', 'patches']:
            while getattr(self.ax, 'artists') != []:
                getattr(self.ax, 'artists')[0].remove()
        self.artifacts = {'artists': [], 'lines': [], 'patches': []}

    def updatePlot(self, attrs):

        self.statusbar.SetStatusText('Plotting... (Please Be Patient)')
        self.ax.clear()
        self.plot(attrs)
        self.statusbar.SetStatusText('Ready')

    def plot(self, param):
        # Create Map
        if not param:
            self.ax.stock_img()
            self.figure.canvas.draw()

            # # Create a really simple plot to fill the void on app startup.
            # self.map = Basemap(ax=self.ax, resolution=RESOLUTION_MAP['Crude'],
            #                    ellps='WGS84', suppress_ticks=True)
            # self.map.bluemarble()
            # self._plot_meridians_and_parallels()

            # # add the motion display to the list of texts for the new axes
            # if self.motion_display:
            #     self.ax.texts.append(self.motion_display)
            # self.figure.canvas.draw()
            return

        # projection_name = param['Projection']
        # projection_key = revlookup(PROJECTIONS, 'name', projection_name)
        # resolution = RESOLUTION_MAP[param['Resolution']]
        # kwargs = {'projection': projection_key,
        #           'ellps': 'WGS84',
        #           'suppress_ticks': True,
        #           'ax': self.ax,
        #           'resolution': resolution}
        # for p in PROJECTION_PARAMS[projection_key].keys():
        #     if 'rspherex' == p:
        #         kwargs['rsphere'] = (param[OPTIONS['rspherex']['name']],
        #                              param[OPTIONS['rspherey']['name']])
        #     elif 'rspherey' == p:
        #         pass
        #     else:
        #         kwargs[p] = param[OPTIONS[p]['name']]
        # self.map = Basemap(**kwargs)

        # # Draw Circle
        # self.draw_range_circle(param["longitude"],
        #                        param["geodetic_latitude"],
        #                        param["range"],
        #                        color='r',
        #                        alpha=0.5)

        # # Draw Blue Marble Texture
        # if param.get('Blue Marble', False):
        #     self.map.bluemarble()
        # else:
        #     self.map.fillcontinents(color='coral', lake_color='aqua')

        # # Draw Coastlines, borders, lines
        # if param.get('Coastlines', False):
        #     self.map.drawcoastlines()
        # if param.get('State Borders', False):
        #     self.map.drawstates()
        # if param.get('Country Borders', False):
        #     self.map.drawcountries()
        # self._plot_meridians_and_parallels()

        # # add the motion display to the list of texts for the new axes
        # if self.motion_display:
        #     self.ax.texts.append(self.motion_display)

        # self.figure.canvas.draw()

    def draw_great_circle(self, x1, y1, x2, y2, linewidth=.5, color='r'):
        self.map.drawgreatcircle(x1, y1, x2, y2, linewidth, color)

    def draw_text(self,
                  text,
                  x,
                  y,
                  xycoords='data',
                  color='white',
                  size='xx-small'):
        self.ax.add_artist(
            AnnotationBbox(TextArea(text,
                                    minimumdescent=False,
                                    textprops={
                                        'color': color,
                                        'size': size
                                    })(x, y),
                           xycoords='data',
                           frameon=False))

    def draw_image(self, image, x, y, zoom=.1, alpha=.7, xycoords='data'):
        if isinstance(image, PyEmbeddedImage):
            png = pyembeddedimage_to_png(image)
        elif isinstance(image, basestring):
            png = read_png(image)
        else:
            png = image
        self.ax.add_artist(
            AnnotationBbox(OffsetImage(png, zoom=zoom, alpha=alpha), (x, y),
                           xycoords=xycoords,
                           frameon=False))

    def draw_range_circle(self, lat, lon, radius, color='r', alpha=.5):
        # FIXME Use the real radius of the earth at this lat/lon
        self.map.tissot(lon,
                        lat,
                        180. / pi * radius / 6370.,
                        256,
                        facecolor=color,
                        alpha=alpha)

    def _plot_meridians_and_parallels(self):
        """
        Plots meridians and parallels appropriate for the current zoom level
        """

        # if grid is off and labels are off wipe lines
        if not self.coordinate_grid_enabled and not self.grid_labels_enabled:
            self._label_grid([], [])
            self._clear_meridians_and_parallels()
            return

        # FIXME: Get this working for other projections
        if self.map.projection != 'cyl':
            self._clear_meridians_and_parallels()

            # FIXME: Make it an option to turn on/off white or black lines
            if self.coordinate_grid_enabled:
                lats = np.linspace(-90, 90, 10)
                self.parallels_b = self.map.drawparallels(
                    lats,
                    labels=[True, False, False, False],
                    fontsize=GRID_LABEL_FONT_SIZE,
                    linewidth=0.2,
                    dashes=[],
                    color='black')
                self.parallels_w = self.map.drawparallels(
                    lats,
                    labels=[False, False, False, False],
                    linewidth=0.2,
                    dashes=[1, 1],
                    color='white')
                lons = np.linspace(-180, 180, 10)
                self.meridians_b = self.map.drawmeridians(
                    lons,
                    labels=[False, False, False, True],
                    fontsize=GRID_LABEL_FONT_SIZE,
                    linewidth=0.2,
                    dashes=[],
                    color='black')
                self.meridians_w = self.map.drawmeridians(
                    lons,
                    labels=[False, False, False, False],
                    linewidth=0.2,
                    dashes=[1, 1],
                    color='white')

            self._label_grid(lons, lats)
            return

        # get the corners of the viewport
        self.lonll, self.latll, delta_lon, delta_lat = self.ax.viewLim.bounds
        self.latur = self.latll + delta_lat
        self.lonur = self.lonll + delta_lon

        # translate to lat/lon (if needed)
        self.lonll, self.latll = self.map(self.lonll, self.latll, inverse=True)
        self.lonur, self.latur = self.map(self.lonur, self.latur, inverse=True)

        if not self._zoom_is_valid():
            return

        # get the range of lat/lon of the current viewport
        lat_range = self.latur - self.latll
        lon_range = self.lonur - self.lonll

        # get the estimated number of parallels and meridians to plot
        nlons = round(NUM_GRID_LINES * 360.0 / lon_range)
        nlats = round(NUM_GRID_LINES * 180.0 / lat_range)

        found = False
        for kv in LAT_GRID_LOOKUP:
            if nlats <= kv[0]:
                self.nlats = kv[1]
                found = True
                break
        if not found:
            self.nlats = self._scale_grid_by_twos(nlats, self.nlats)

        found = False
        for kv in LON_GRID_LOOKUP:
            if nlons <= kv[0]:
                self.nlons = kv[1]
                found = True
                break
        if not found:
            self.nlons = self._scale_grid_by_twos(nlons, self.nlons)

        # return if we've got too few to plot
        if self.nlons <= 1 or self.nlats <= 1:
            return

        # calculate the actual meridian coordinates
        lons = np.linspace(-180, 180.0, self.nlons)
        lons = np.concatenate((lons - 360.0, lons, lons + 360.0))
        lats = np.linspace(-90.0, 90.0, self.nlats)

        # filter them to speed plotting
        lon_condition = lons >= self.lonll
        lons = np.extract(lon_condition, lons)
        lon_condition = lons <= self.lonur
        lons = np.extract(lon_condition, lons)

        lat_condition = lats >= self.latll
        lats = np.extract(lat_condition, lats)
        lat_condition = lats <= self.latur
        lats = np.extract(lat_condition, lats)

        # plot labels for each one
        self._label_grid(lons, lats)

        self.lats = lats
        self.lons = lons

        # plot new parallels/meridians
        # these are made up of a solid black line, and a dashed white line
        self._clear_meridians_and_parallels()

        # FIXME: Make it an option to turn on/off white or black lines
        if self.coordinate_grid_enabled:
            self.parallels_b = self.map.drawparallels(
                lats,
                labels=[False, False, False, False],
                linewidth=0.2,
                dashes=[],
                color='black')
            self.parallels_w = self.map.drawparallels(
                lats,
                labels=[False, False, False, False],
                linewidth=0.2,
                dashes=[1, 1],
                color='white')
            self.meridians_b = self.map.drawmeridians(
                lons,
                labels=[False, False, False, False],
                linewidth=0.2,
                dashes=[],
                color='black')
            self.meridians_w = self.map.drawmeridians(
                lons,
                labels=[False, False, False, False],
                linewidth=0.2,
                dashes=[1, 1],
                color='white')

    def _label_grid(self, lons, lats):
        """
        Plots labels on grid lines.
        """
        degree_sign = u'\N{DEGREE SIGN}'
        for label in self.labels:
            if label in self.ax.texts:
                label.remove()
        self.labels = []

        if self.grid_labels_enabled:

            # peg the corners on the projection boundaries
            lonll = max(self.lonll, self.map.llcrnrlon)
            lonur = min(self.lonur, self.map.urcrnrlon)
            latll = max(self.latll, self.map.llcrnrlat)
            latur = min(self.latur, self.map.urcrnrlat)

            for lat in lats:
                if lat > latur or lat < latll:
                    continue
                if lat >= 0:
                    NS = 'N'
                else:
                    NS = 'S'
                (d, m, sd) = self._to_dms(lat)
                self.labels.append(
                    self.ax.text(lonll,
                                 lat,
                                 "%02d%s%02d'%05.2f\"%s" %
                                 (abs(d), degree_sign, m, sd, NS),
                                 fontproperties=self.grid_label_font,
                                 verticalalignment='center',
                                 horizontalalignment='left',
                                 bbox=dict(facecolor='white',
                                           alpha=0.5,
                                           pad=0.2,
                                           lw=0.2)))

            for lon in lons:
                if lon > lonur or lon < lonll:
                    continue

                # normalize
                display_lon = self.wrap_lon_at_day_boundary(lon)
                if display_lon >= 0:
                    NS = 'E'
                else:
                    NS = 'W'
                (d, m, sd) = self._to_dms(display_lon)
                self.labels.append(
                    self.ax.text(lon,
                                 latll,
                                 "%03d%s%02d'%05.2f\"%s" %
                                 (abs(d), degree_sign, m, sd, NS),
                                 fontproperties=self.grid_label_font,
                                 rotation='vertical',
                                 verticalalignment='bottom',
                                 horizontalalignment='center',
                                 bbox=dict(facecolor='white',
                                           alpha=0.5,
                                           pad=0.2,
                                           lw=0.2)))

    def _to_dms(self, deg):
        """
        Converts degrees to degrees minutes seconds
        """
        d = int(deg)
        md = abs(deg - d) * 60.0
        m = int(md)
        sd = (md - m) * 60.0
        return [d, m, sd]

    def _scale_grid_by_twos(self, n_target, n_current):
        """
        Determines number of grid lines to plot via hopping by powers of 2
        """
        half_n_current = int(n_current / 2.0) + 1
        twice_n_current = round(n_current * 2) - 1
        if n_target < n_current and n_target > 1:
            while n_target < n_current:
                n_current = half_n_current
                half_n_current = int(n_current / 2.0) + 1
                if n_current < 3:
                    break
        else:
            while n_target >= twice_n_current:
                n_current = twice_n_current
                twice_n_current = round(n_current * 2) - 1

        # should be an odd number at this point, but make sure
        if n_current % 2 == 0:
            n_current += 1

        return n_current

    def _zoom_is_valid(self):
        """
        Checks if zoom level is valid
        """
        # check if corners are valid
        try:
            delta_lon = self.lonur - self.lonll
            delta_lat = self.latur - self.latll
        except:
            return False
        return True

    def _clear_meridians_and_parallels(self):
        """
        Clears currently plotted meridians and parallels
        """
        # clear old meridians and parallels
        # these are made up of a solid black line, and a dashed white line
        for par in self.parallels_w:
            for item in self.parallels_w[par]:
                if len(item) > 0:
                    if item[0] in self.ax.lines:
                        item[0].remove()
        self.parallels_w.clear()

        for par in self.parallels_b:
            for item in self.parallels_b[par]:
                if len(item) > 0:
                    if item[0] in self.ax.lines:
                        item[0].remove()
        self.parallels_b.clear()

        for mer in self.meridians_w:
            for item in self.meridians_w[mer]:
                if len(item) > 0:
                    if item[0] in self.ax.lines:
                        item[0].remove()
        self.meridians_w.clear()

        for mer in self.meridians_b:
            for item in self.meridians_b[mer]:
                if len(item) > 0:
                    if item[0] in self.ax.lines:
                        item[0].remove()
        self.meridians_b.clear()
Exemplo n.º 4
0
class PlotPanel(wx.Panel):
    zoom_levels = [100.0, 110.0, 125.0, 150.0, 200.0, 250.0, 300.0, 400.0]
    dose_contour_levels = [
        10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 95.0, 98 - 0,
        100.0, 102.0
    ]

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.is_closeable = False
        self.parent = parent
        self.active_plan = None
        self.plot_mouse_action = None

        pub.subscribe(self.on_patient_loaded, "patient.loaded.ini")
        pub.subscribe(self.set_active_image, "plot.image.active_id")
        pub.subscribe(self.voi_changed, "voi.selection_changed")
        pub.subscribe(self.plan_changed, "plan.active.changed")
        pub.subscribe(self.plan_field_changed, "plan.field.selection_changed")
        pub.subscribe(self.plan_dose_changed, "plan.dose.active_changed")
        pub.subscribe(self.plan_dose_removed, "plan.dose.removed")
        pub.subscribe(self.plan_let_added, "plan.let.added")
        pub.subscribe(self.plan_let_removed, "plan.let.removed")
        pub.subscribe(self.target_dose_changed,
                      "plan.dose.target_dose_changed")
        self.plotmode = "Transversal"

    def __del__(self):
        pub.unsubscribe(self.on_patient_loaded, "patient.loaded.ini")
        pub.unsubscribe(self.set_active_image, "plot.image.active_id")
        pub.unsubscribe(self.voi_changed, "voi.selection_changed")
        pub.unsubscribe(self.plan_changed, "plan.active.changed")
        pub.unsubscribe(self.plan_field_changed,
                        "plan.field.selection_changed")
        pub.unsubscribe(self.plan_dose_changed, "plan.dose.active_changed")
        pub.unsubscribe(self.plan_dose_removed, "plan.dose.removed")
        pub.unsubscribe(self.plan_let_added, "plan.let.added")
        pub.unsubscribe(self.plan_let_removed, "plan.let.removed")
        pub.unsubscribe(self.target_dose_changed,
                        "plan.dose.target_dose_changed")

    def target_dose_changed(self, msg):
        self.Draw()

    def Init(self):
        self.plotutil = PlotUtil()
        self.figure = Figure(None, 100)
        self.canvas = FigureCanvasWxAgg(self, -1, self.figure)
        # ~ self.canvas.SetDoubleBuffered(True)
        self.clear()
        self.plotutil.set_draw_in_gui(True)
        self.figure.set_frameon(True)
        rect = self.figure.patch
        rect.set_facecolor('black')

    def plan_dose_changed(self, msg):
        if msg.data["plan"] is self.active_plan:
            self.plotutil.set_dose(msg.data["dose"].get_dosecube())
            self.Draw()

    def plan_field_changed(self, msg):
        self.Draw()

    def plan_dose_removed(self, msg):
        if msg.data["plan"] is self.active_plan:
            self.plotutil.set_dose(None)
            self.Draw()

    def plan_let_added(self, msg):
        if msg.data["plan"] is self.active_plan:
            self.plotutil.set_let(msg.data["let"])
            self.Draw()

    def plan_let_removed(self, msg):
        if msg.data["plan"] is self.active_plan:
            self.plotutil.set_let(None)
            self.Draw()

    def plan_changed(self, msg):
        self.active_plan = msg.data
        if self.active_plan is None:
            self.plotutil.set_plan(None)
            self.plotutil.set_dose(None)
            self.plotutil.set_let(None)
        else:
            self.plotutil.set_plan(self.active_plan)
            doseobj = self.active_plan.get_dose()

            if doseobj is not None:
                self.plotutil.set_dose(doseobj.get_dosecube())
            else:
                self.plotutil.set_dose(None)
            self.plotutil.set_let(self.active_plan.get_let())
        self.Draw()

    def set_toolbar(self, toolbar):
        id = wx.NewId()
        selector = wx.Choice(toolbar, id)
        selector.Append("Transversal")
        selector.Append("Sagital")
        selector.Append("Coronal")
        idx = selector.FindString(self.plotmode)
        selector.Select(idx)

        toolbar.AddControl(selector)
        wx.EVT_CHOICE(selector, id, self.plot_mode_changed)

        id = wx.NewId()
        self.zoom_in_btn = toolbar.AddLabelTool(
            id, '', wx.Bitmap(get_resource_path('zoom_in.png')))
        wx.EVT_MENU(toolbar, id, self.zoom_in)

        id = wx.NewId()
        self.zoom_out_btn = toolbar.AddLabelTool(
            id, '', wx.Bitmap(get_resource_path('zoom_out.png')))
        wx.EVT_MENU(toolbar, id, self.zoom_out)

    def zoom_buttons_visible(self):
        zoom_idx = self.zoom_levels.index(self.plotutil.get_zoom())
        self.zoom_in_btn.Enable(True)
        self.zoom_out_btn.Enable(True)

        if len(self.zoom_levels) == zoom_idx + 1:
            self.zoom_in_btn.Enable(False)
        if zoom_idx == 0:
            self.zoom_out_btn.Enable(False)

    def zoom_in(self, evt):
        zoom_idx = self.zoom_levels.index(self.plotutil.get_zoom())
        zoom_idx += 1
        if len(self.zoom_levels) > zoom_idx:
            zoom = self.zoom_levels[zoom_idx]
            self.plotutil.set_zoom(zoom)
            self.zoom_buttons_visible()
            self.Draw()

    def zoom_out(self, evt):
        zoom_idx = self.zoom_levels.index(self.plotutil.get_zoom())
        zoom_idx -= 1
        if zoom_idx >= 0:
            zoom = self.zoom_levels[zoom_idx]
            self.plotutil.set_zoom(zoom)
            self.zoom_buttons_visible()
            self.Draw()

    def plot_mode_changed(self, evt):
        self.plotmode = evt.GetString()
        self.plotutil.set_plot_plan(self.plotmode)
        self.image_idx = int(self.plotutil.get_images_count() / 2)
        self.clear()
        self.Draw()

    def clear(self):
        self.figure.clear()
        self.subplot = self.figure.add_subplot(111)
        self.plotutil.set_figure(self.subplot)

    def voi_changed(self, msg):
        voi = msg.data
        if voi.is_selected():
            self.plotutil.add_voi(voi.voxelplan_voi)
        else:
            self.plotutil.remove_voi(voi.voxelplan_voi)
        self.Draw()

    def set_active_image(self, msg):
        if self.plotmode == "Transversal":
            self.image_idx = msg.data
            self.Draw()

    def on_patient_loaded(self, msg):
        self.data = msg.data
        ctx = self.data.get_images().get_voxelplan()

        self.plotutil.set_ct(ctx)

        self.image_idx = int(ctx.dimz / 2)
        self.setSize()
        self.bind_keys()

    def get_figure(self):
        return self.figure

    def bind_keys(self):
        self.Bind(wx.EVT_MOUSEWHEEL, self.on_mouse_wheel)
        self.Bind(wx.EVT_ENTER_WINDOW, self.on_mouse_enter)
        self.Bind(wx.EVT_LEAVE_WINDOW, self.on_mouse_leave)

        self.Bind(wx.EVT_SIZE, self.on_size)
        self.canvas.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
        self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move_plot)
        self.canvas.mpl_connect('button_press_event', self.on_mouse_press_plot)
        self.canvas.mpl_connect('button_release_event',
                                self.on_mouse_action_ended)
        self.canvas.mpl_connect('figure_leave_event',
                                self.on_mouse_action_ended)

    def on_mouse_press_plot(self, evt):
        if evt.button is 3:
            pos = evt.guiEvent.GetPosition()
            standard = True
            if hasattr(self.plotutil, "contrast_bar"):
                bar = self.plotutil.contrast_bar
                if evt.inaxes is bar.ax:
                    menu = self.right_click_contrast()
                    standard = False
            if hasattr(self.plotutil, "dose_bar"):
                bar = self.plotutil.dose_bar
                if evt.inaxes is bar.ax:
                    menu = self.right_click_dose()
                    standard = False

            if standard:
                menu = self.normal_right_click_menu()

            wx.CallAfter(self.show_menu, menu)
            if self.canvas.HasCapture():
                self.canvas.ReleaseMouse()

        elif evt.button is 1:
            if hasattr(self.plotutil, "contrast_bar"):
                bar = self.plotutil.contrast_bar
                if evt.inaxes is bar.ax:
                    if evt.ydata >= 0.50:
                        self.plot_mouse_action = "contrast_top"
                    else:
                        self.plot_mouse_action = "contrast_bottom"
            if hasattr(self.plotutil, "dose_bar"):
                bar = self.plotutil.dose_bar
                if evt.inaxes is bar.ax:
                    if evt.ydata >= 0.50:
                        self.plot_mouse_action = "dose_top"
                    else:
                        self.plot_mouse_action = "dose_bottom"
            if hasattr(self.plotutil, "let_bar"):
                bar = self.plotutil.let_bar
                if evt.inaxes is bar.ax:
                    if evt.ydata >= 0.50:
                        self.plot_mouse_action = "let_top"
                    else:
                        self.plot_mouse_action = "let_bottom"

        self.mouse_pos_ini = [evt.x, evt.y]
        evt.guiEvent.Skip()

    def show_menu(self, menu):
        self.PopupMenu(menu)

    def on_mouse_action_ended(self, evt):
        self.plot_mouse_action = None

    def on_mouse_move_plot(self, evt):
        pos = [evt.x, evt.y]
        if self.plot_mouse_action is not None:
            step = [
                pos[0] - self.mouse_pos_ini[0], pos[1] - self.mouse_pos_ini[1]
            ]
            if self.plot_mouse_action == "contrast_top":
                contrast = self.plotutil.get_contrast()
                stepsize = np.log(contrast[1] - contrast[0])
                contrast[1] -= stepsize * step[1]
                self.plotutil.set_contrast(contrast)
            elif self.plot_mouse_action == "contrast_bottom":
                contrast = self.plotutil.get_contrast()
                stepsize = np.log(contrast[1] - contrast[0])
                contrast[0] -= stepsize * step[1]
                self.plotutil.set_contrast(contrast)
            elif self.plot_mouse_action == "dose_top":
                dose = self.plotutil.get_min_max_dose()
                dose[1] -= 0.30 * step[1]
                self.plotutil.set_dose_min_max(dose)
            elif self.plot_mouse_action == "dose_bottom":
                dose = self.plotutil.get_min_max_dose()
                dose[0] -= 0.30 * step[1]
                self.plotutil.set_dose_min_max(dose)
            elif self.plot_mouse_action == "let_top":
                let = self.plotutil.get_min_max_let()
                let[1] -= 0.30 * step[1]
                self.plotutil.set_let_min_max(let)
            elif self.plot_mouse_action == "let_bottom":
                let = self.plotutil.get_min_max_let()
                let[0] -= 0.30 * step[1]
                self.plotutil.set_let_min_max(let)
            self.Draw()
        elif evt.button == 1 and evt.inaxes is self.plotutil.fig_ct.axes:
            if not None in self.mouse_pos_ini:
                step = [
                    pos[0] - self.mouse_pos_ini[0],
                    pos[1] - self.mouse_pos_ini[1]
                ]
                if self.plotutil.move_center(step):
                    self.Draw()

        self.mouse_pos_ini = [evt.x, evt.y]
        if hasattr(self.plotutil,
                   "fig_ct") and evt.inaxes is self.plotutil.fig_ct.axes:
            point = self.plotutil.pixel_to_pos(
                [round(evt.xdata), round(evt.ydata)])

            text = "X: %.2f mm Y: %.2f mm / X: %d px Y: %d px" % (
                point[1][0], point[1][1], point[0][0], point[0][1])
            pub.sendMessage("statusbar.update", {"number": 1, "text": text})
            dim = self.data.get_image_dimensions()
            if self.plotmode == "Transversal":
                pos = [round(evt.xdata), round(evt.ydata), self.image_idx]
            elif self.plotmode == "Sagital":
                pos = [
                    dim[0] - round(evt.xdata), self.image_idx,
                    dim[2] - round(evt.ydata)
                ]
            elif self.plotmode == "Coronal":
                pos = [
                    self.image_idx, dim[1] - round(evt.xdata),
                    dim[2] - round(evt.ydata)
                ]
            try:
                ct_value = self.data.get_image_cube()[pos[2], pos[1], pos[0]]
                text = "Value: %.1f" % (ct_value)
                plan = self.active_plan
                if plan is not None:
                    dose = plan.get_dose_cube()
                    if dose is not None:
                        dose_value = dose[pos[2], pos[1], pos[0]]
                        target_dose = plan.get_dose().get_dose()
                        if not target_dose == 0.0:
                            dose_value *= target_dose / 1000
                            text += " / Dose: %.1f Gy" % (float(dose_value))
                        else:
                            dose_value /= 10
                            text += " / Dose: %.1f %%" % (float(dose_value))

                    let = plan.get_let_cube()
                    if let is not None:
                        let_value = let[pos[2], pos[1], pos[0]]
                        text += " / LET: %.1f kev/um" % (let_value)
            except IndexError as e:
                pass
            pub.sendMessage("statusbar.update", {"number": 2, "text": text})

    def normal_right_click_menu(self):
        menu = wx.Menu()
        voi_menu = wx.Menu()

        for voi in self.data.get_vois():
            id = wx.NewId()
            item = voi_menu.AppendCheckItem(id, voi.get_name())
            if voi.is_selected():
                item.Check()
            wx.EVT_MENU(self, id, self.menu_voi_selected)
        if voi_menu.GetMenuItemCount() > 0:
            menu.AppendSubMenu(voi_menu, "Vois")
        view_menu = wx.Menu()

        active_plan = self.active_plan
        if active_plan is not None:
            dose = active_plan.get_dose()
            dose_type_menu = wx.Menu()
            if dose is not None:
                id = wx.NewId()
                item = view_menu.AppendCheckItem(id, "View Dose")
                if self.plotutil.get_dose() is not None:
                    item.Check()
                wx.EVT_MENU(self, id, self.toogle_dose)

                id = wx.NewId()
                item = dose_type_menu.Append(id, "Color wash")
                wx.EVT_MENU(self, id, self.change_dose_to_colorwash)

                id = wx.NewId()
                item = dose_type_menu.Append(id, "Contour")
                wx.EVT_MENU(self, id, self.change_dose_to_contour)

                menu.AppendSubMenu(dose_type_menu, "Dose Visalization")
                if self.plotutil.get_dose_plot() == "contour":
                    dose_contour_menu = wx.Menu()
                    for level in self.dose_contour_levels:
                        id = wx.NewId()
                        item = dose_contour_menu.AppendCheckItem(
                            id, "%d %%" % level)
                        for contour in self.plotutil.get_dose_contours():
                            if contour["doselevel"] == level:
                                item.Check()
                        wx.EVT_MENU(self, id, self.toogle_dose_contour)
                    menu.AppendSubMenu(dose_contour_menu,
                                       "Dose Contour levels")

            let = active_plan.get_let()

            if let is not None:
                id = wx.NewId()
                item = view_menu.AppendCheckItem(id, "View LET")
                if self.plotutil.get_let() is not None:
                    item.Check()
                wx.EVT_MENU(self, id, self.toogle_let)

            if view_menu.GetMenuItemCount() > 0:
                menu.AppendSubMenu(view_menu, "View")

            field_menu = wx.Menu()
            for field in active_plan.get_fields():
                id = wx.NewId()
                item = field_menu.AppendCheckItem(id, field.get_name())
                if field.is_selected():
                    item.Check()
                wx.EVT_MENU(self, id, self.menu_field_selected)
            if field_menu.GetMenuItemCount() > 0:
                menu.AppendSubMenu(field_menu, "Fields")

        jump_menu = wx.Menu()
        id = wx.NewId()
        item = jump_menu.Append(id, "First")
        wx.EVT_MENU(self, id, self.jump_to_first)

        id = wx.NewId()
        item = jump_menu.Append(id, "Middle")
        wx.EVT_MENU(self, id, self.jump_to_middle)

        id = wx.NewId()
        item = jump_menu.Append(id, "Last")
        wx.EVT_MENU(self, id, self.jump_to_last)

        menu.AppendSubMenu(jump_menu, "Jump To")
        return menu

    def right_click_dose(self):
        menu = wx.Menu()
        id = wx.NewId()
        item = menu.Append(id, "Reset")
        wx.EVT_MENU(menu, id, self.reset_dose_range)

        colormap_menu = wx.Menu()

        id = wx.NewId()
        colormap_menu.Append(id, "Continuous")
        wx.EVT_MENU(colormap_menu, id, self.set_colormap_dose)

        id = wx.NewId()
        colormap_menu.Append(id, "Discrete")
        wx.EVT_MENU(colormap_menu, id, self.set_colormap_dose)

        item = menu.AppendSubMenu(colormap_menu, "Colorscale")

        scale_menu = wx.Menu()

        id = wx.NewId()
        scale_menu.Append(id, "Auto")
        wx.EVT_MENU(scale_menu, id, self.set_dose_scale)

        if self.active_plan.get_dose().get_dose() > 0.0:
            id = wx.NewId()
            scale_menu.Append(id, "Absolute")
            wx.EVT_MENU(scale_menu, id, self.set_dose_scale)

        id = wx.NewId()
        scale_menu.Append(id, "Relative")
        wx.EVT_MENU(scale_menu, id, self.set_dose_scale)

        item = menu.AppendSubMenu(scale_menu, "Scale")

        return menu

    def right_click_contrast(self):
        menu = wx.Menu()
        id = wx.NewId()
        item = menu.Append(id, "Reset")
        wx.EVT_MENU(menu, id, self.reset_contrast)
        return menu

    def set_colormap_dose(self, evt):
        colormap = plt.get_cmap(None)
        name = evt.GetEventObject().GetLabel(evt.GetId())
        if name == "Discrete":
            colormap = cmap_discretize(colormap, 10)
        self.plotutil.set_colormap_dose(colormap)
        self.Draw()

    def set_dose_scale(self, evt):
        scale = {"auto": "auto", "absolute": "abs", "relative": "rel"}
        name = evt.GetEventObject().GetLabel(evt.GetId())
        self.plotutil.set_dose_axis(scale[name.lower()])
        self.Draw()

    def reset_dose_range(self, evt):
        self.plotutil.set_dose_min_max(0, 100)
        self.Draw()

    def reset_contrast(self, evt):
        contrast = [-100, 400]
        self.plotutil.set_contrast(contrast)
        self.Draw()

    def jump_to_first(self, evt):
        self.image_idx = 0
        self.Draw()

    def jump_to_middle(self, evt):
        self.image_idx = self.plotutil.get_images_count() / 2
        self.Draw()

    def jump_to_last(self, evt):
        self.image_idx = self.plotutil.get_images_count() - 1
        self.Draw()

    def toogle_dose_contour(self, evt):
        value = float(evt.GetEventObject().GetLabel(evt.GetId()).split()[0])
        if evt.IsChecked():
            self.plotutil.add_dose_contour({"doselevel": value, "color": "b"})
        else:
            for contour in self.plotutil.get_dose_contours():
                if contour["doselevel"] == value:
                    self.plotutil.remove_dose_contour(contour)
        self.Draw()

    def toogle_dose(self, evt):
        if self.plotutil.get_dose() is None:
            self.plotutil.set_dose(self.active_plan.get_dose().get_dosecube())
        else:
            self.plotutil.set_dose(None)
        self.Draw()

    def toogle_let(self, evt):
        if self.plotutil.get_let() is None:
            self.plotutil.set_let(self.active_plan.get_let())
        else:
            self.plotutil.set_let(None)
        self.Draw()

    def menu_voi_selected(self, evt):
        name = evt.GetEventObject().GetLabel(evt.GetId())
        name = name.replace("__", "_")
        voi = self.data.get_vois().get_voi_by_name(name)
        if not voi is None:
            voi.toogle_selected()

    def menu_field_selected(self, evt):
        name = evt.GetEventObject().GetLabel(evt.GetId())
        field = self.active_plan.get_fields().get_field_by_name(name)
        field.toogle_selected(self.active_plan)

    def change_dose_to_colorwash(self, evt):
        self.plotutil.set_dose_plot("colorwash")
        self.Draw()

    def change_dose_to_contour(self, evt):
        self.plotutil.set_dose_plot("contour")
        self.Draw()

    def on_size(self, evt):
        """Refresh the view when the size of the panel changes."""

        self.setSize()

    def on_mouse_wheel(self, evt):
        delta = evt.GetWheelDelta()
        rot = evt.GetWheelRotation()
        rot = rot / delta

        if evt.ControlDown():
            if (rot >= 1):
                self.zoom_in(None)
            elif (rot < 1):
                self.zoom_out(None)
            return
        n_images = self.data.get_images().get_voxelplan().dimz
        if n_images:
            if (rot >= 1):
                if (self.image_idx > 0):
                    self.image_idx -= 1
                    self.Draw()
            if (rot <= -1):
                if (self.image_idx < self.plotutil.get_images_count() - 1):
                    self.image_idx += 1
                    self.Draw()

    def on_key_down(self, evt):
        prevkey = [wx.WXK_UP, wx.WXK_PAGEUP]
        nextkey = [wx.WXK_DOWN, wx.WXK_PAGEDOWN]
        code = evt.GetKeyCode()
        if code in prevkey:
            if (self.image_idx > 0):
                self.image_idx -= 1
                self.Draw()
        elif code in nextkey:
            if (self.image_idx < self.plotutil.get_images_count() - 1):
                self.image_idx += 1
                self.Draw()

    def on_mouse_enter(self, evt):
        """Set a flag when the cursor enters the window."""
        self.mouse_in_window = True

    def on_mouse_leave(self, evt):
        """Set a flag when the cursor leaves the window."""

        self.mouse_in_window = False

    def setSize(self):
        size = self.parent.GetClientSize()
        size[1] = size[1] - 40
        size[0] = size[0] - 5
        pixels = tuple(size)
        self.canvas.SetSize(pixels)
        self.figure.set_size_inches(
            float(pixels[0]) / self.figure.get_dpi(),
            float(pixels[1]) / self.figure.get_dpi())
        self.Draw()

    def Draw(self):
        self.plotutil.plot(self.image_idx)

        self.figure.subplots_adjust(left=0,
                                    bottom=0,
                                    right=1,
                                    top=1,
                                    wspace=None,
                                    hspace=None)
        # self.figure.tight_layout(pad=0.0)

        if hasattr(self.plotutil, "dose_bar"):
            bar = self.plotutil.dose_bar
            bar.ax.yaxis.label.set_color('white')
            bar.ax.tick_params(axis='y', colors='white', labelsize=8)
        if hasattr(self.plotutil, "let_bar"):
            bar = self.plotutil.let_bar
            bar.ax.yaxis.label.set_color('white')
            bar.ax.yaxis.label.set_color('white')
            bar.ax.tick_params(axis='y', colors='white', labelsize=8)
        if hasattr(self.plotutil, "contrast_bar"):
            bar = self.plotutil.contrast_bar
            bar.ax.yaxis.label.set_color('white')
            bar.ax.yaxis.set_label_position('left')
            [t.set_color("white") for t in bar.ax.yaxis.get_ticklabels()]
            [t.set_size(8) for t in bar.ax.yaxis.get_ticklabels()]

            # bar.ax.tick_params(axis='y', colors='white',labelsize=8,labelleft=True,labelright=False)

        self.canvas.draw()
Exemplo n.º 5
0
class PlotPanel(wx.Panel):
    ''' Base class for the plotting in GenX - all the basic functionallity
        should be implemented in this class. The plots should be derived from
        this class. These classes should implement an update method to update
        the plots.
    '''
    def __init__(self, parent, id = -1, color = None, dpi = None
            , style = wx.NO_FULL_REPAINT_ON_RESIZE|wx.EXPAND|wx.ALL
            , config = None, config_name = '', **kwargs):

        wx.Panel.__init__(self,parent, id = id, style = style, **kwargs)

        self.parent = parent
        self.callback_window = self
        self.config = config
        self.config_name = config_name
        self.figure = Figure(None,dpi)
        self.canvas = FigureCanvasWxAgg(self, -1, self.figure)
        self.canvas.SetExtraStyle(wx.EXPAND)
        self.SetColor(color)
        self.Bind(wx.EVT_IDLE, self._onIdle)
        self.Bind(wx.EVT_SIZE, self._onSize)
        self._resizeflag = True
        self.print_size = (15./2.54, 12./2.54)
        #self._SetSize()

        # Flags and bindings for zooming
        self.zoom = False
        self.zooming = False
        self.scale = 'linear'
        self.autoscale = True


        self.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnLeftMouseButtonDown)
        self.canvas.Bind(wx.EVT_LEFT_UP, self.OnLeftMouseButtonUp)
        self.canvas.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDblClick)
        self.canvas.Bind(wx.EVT_RIGHT_UP, self.OnContextMenu)

        cursor = wx.StockCursor(wx.CURSOR_CROSS)
        self.canvas.SetCursor(cursor)
        self.old_scale_state = True
        self.ax = None

        # Init printout stuff
        self.fig_printer = FigurePrinter(self)

        # Create the drawing bitmap
        self.bitmap =wx.EmptyBitmap(1, 1)
#        DEBUG_MSG("__init__() - bitmap w:%d h:%d" % (w,h), 2, self)
        self._isDrawn = False

    def SetColor(self, rgbtuple=None):
        ''' Set the figure and canvas color to be the same '''
        if not rgbtuple:
            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
        col = [c/255. for c in rgbtuple]
        self.figure.set_facecolor(col)
        self.figure.set_edgecolor(col)
        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))

    def _onSize(self, evt):
        self._resizeflag = True
        self._SetSize()
        #self.canvas.draw(repaint = False)

    def _onIdle(self, evt):
        if self._resizeflag:
            self._resizeflag = False
            self._SetSize()
            #self.canvas.gui_repaint(drawDC = wx.PaintDC(self))


    def _SetSize(self, pixels = None):
        ''' This method can be called to force the Plot to be a desired
            size which defaults to the ClientSize of the Panel.
        '''
        if not pixels:
            pixels = self.GetClientSize()

        self.canvas.SetSize(pixels)
        #self.figure.set_size_inches(pixels[0]/self.figure.get_dpi()
        #, pixels[1]/self.figure.get_dpi())

    def ReadConfig(self):
        '''ReadConfig(self) --> None

        Reads in the config file
        '''
        bool_items = ['zoom', 'autoscale']
        bool_func = [self.SetZoom, self.SetAutoScale]

        if not self.config:
            return


        vals = []
        for index in range(len(bool_items)):
            try:
                val = self.config.get_boolean(self.config_name,\
                        bool_items[index])
            except io.OptionError as e:
                print('Could not locate option %s.%s'\
                %(self.config_name, bool_items[index]))
                vals.append(None)
            else:
                vals.append(val)

        try:
            scale = self.config.get(self.config_name, 'y scale')
            string_sucess = True
        except io.OptionError as e:
            string_sucess = False
            print('Could not locate option %s.%s'\
            %(self.config_name, 'scale'))
        else:
            self.SetYScale(scale)

        # This is done due to that the zoom and autoscale has to read
        # before any commands are issued in order not to overwrite
        # the config
        [bool_func[i](vals[i]) for i in range(len(vals)) if vals[i]]


    def WriteConfig(self):
        '''WriteConfig(self) --> None

        Writes the current settings to the config file
        '''
        if self.config:
            self.config.set(self.config_name, 'zoom', self.GetZoom())
            self.config.set(self.config_name, 'autoscale', self.GetAutoScale())
            self.config.set(self.config_name, 'y scale', self.GetYScale())

    def SetZoom(self, active = False):
        '''
        set the zoomstate
        '''
        #if not self.zoom_sel:
            #self.zoom_sel = RectangleSelector(self.ax,\
            # self.box_select_callback, drawtype='box',useblit=False)
        #print help(self.zoom_sel.ignore)

        if active:
            #self.zoom_sel.ignore = lambda x: False
            self.zoom = True
            cursor = wx.StockCursor(wx.CURSOR_MAGNIFIER)
            self.canvas.SetCursor(cursor)
            if self.callback_window:
                evt = state_changed(zoomstate = True,\
                        yscale = self.GetYScale(), autoscale = self.autoscale)
                wx.PostEvent(self.callback_window, evt)
            if self.ax:
                #self.ax.set_autoscale_on(False)
                self.old_scale_state = self.GetAutoScale()
                self.SetAutoScale(False)

        else:
            #self.zoom_sel.ignore = lambda x: True
            self.zoom = False
            cursor = wx.StockCursor(wx.CURSOR_CROSS)
            self.canvas.SetCursor(cursor)
            if self.callback_window:
                evt = state_changed(zoomstate = False,\
                    yscale = self.GetYScale(), autoscale = self.autoscale)
                wx.PostEvent(self.callback_window, evt)
            if self.ax:
                #self.ax.set_autoscale_on(self.autoscale)
                self.SetAutoScale(self.old_scale_state)
        self.WriteConfig()

    def GetZoom(self):
        '''GetZoom(self) --> state [bool]
        Returns the zoom state of the plot panel.
        True = zoom active
        False = zoom inactive
        '''
        return self.zoom

    def SetAutoScale(self, state):
        '''SetAutoScale(self, state) --> None

        Sets autoscale of the main axes wheter or not it should autoscale
        when plotting
        '''
        #self.ax.set_autoscale_on(state)
        self.autoscale = state
        self.WriteConfig()
        evt = state_changed(zoomstate = self.GetZoom(),\
                        yscale = self.GetYScale(), autoscale = self.autoscale)
        wx.PostEvent(self.callback_window, evt)


    def GetAutoScale(self):
        '''GetAutoScale(self) --> state [bool]

        Returns the autoscale state, true if the plots is automatically
        scaled for each plot command.
        '''
        return self.autoscale

    def AutoScale(self, force = False):
        '''AutoScale(self) --> None

        A log safe way to autoscale the plots - the ordinary axis tight
        does not work for negative log data. This works!
        '''
        if not (self.autoscale or force):
            return
        # If nothing is plotted no autoscale use defaults...
        if sum([len(line.get_ydata()) > 0 for line in self.ax.lines]) == 0:
            self.ax.set_xlim(0, 1)
            self.ax.set_ylim(1e-3, 1.0)
            return
        if self.scale == 'log':
            #print 'log scaling'
            # Find the lowest possible value of all the y-values that are
            #greater than zero. check so that y data contain data before min
            # is applied
            tmp = [line.get_ydata().compress(line.get_ydata() > 0.0).min()\
                   for line in self.ax.lines if array(line.get_ydata() > 0.0).sum() > 0]
            if len(tmp) > 0:
                ymin = min(tmp)
            else:
                ymin = 1e-3
            tmp = [line.get_ydata().compress(line.get_ydata() > 0.0).max()\
                   for line in self.ax.lines if array(line.get_ydata() > 0.0).sum() > 0]
            if len(tmp) > 0:
                ymax = max(tmp)
            else:
                ymax = 1
        else:
            ymin = min([array(line.get_ydata()).min()\
                     for line in self.ax.lines if len(line.get_ydata()) > 0])
            ymax = max([array(line.get_ydata()).max()\
                   for line in self.ax.lines if len(line.get_ydata()) > 0])
        tmp = [array(line.get_xdata()).min()\
                    for line in self.ax.lines if len(line.get_ydata()) > 0]
        if len(tmp) > 0:
            xmin = min(tmp)
        else:
            xmin = 0
        tmp = [array(line.get_xdata()).max()\
                    for line in self.ax.lines if len(line.get_ydata()) > 0]
        if len(tmp) > 0:
            xmax = max(tmp)
        else:
            xmax = 1
        # Set the limits
        #print 'Autoscaling to: ', ymin, ymax
        self.ax.set_xlim(xmin, xmax)
        self.ax.set_ylim(ymin*(1-sign(ymin)*0.05), ymax*(1+sign(ymax)*0.05))
        #self.ax.set_yscale(self.scale)
        self.flush_plot()

    def SetYScale(self, scalestring):
        ''' SetYScale(self, scalestring) --> None

        Sets the y-scale of the main plotting axes. Currently accepts
        'log' or 'lin'.
        '''
        if self.ax:
            if scalestring == 'log':
                self.scale = 'log'
                self.AutoScale(force = True)
                try:
                    self.ax.set_yscale('log')
                except OverflowError:
                    self.AutoScale(force = True)
            elif scalestring == 'linear' or scalestring == 'lin':
                self.scale = 'linear'
                self.ax.set_yscale('linear')
                self.AutoScale(force = True)
            else:
                raise ValueError('Not allowed scaling')
            self.flush_plot()
            evt = state_changed(zoomstate = self.GetZoom(),\
                        yscale = self.GetYScale(), autoscale = self.autoscale)
            wx.PostEvent(self.callback_window, evt)
            self.WriteConfig()

    def GetYScale(self):
        '''GetYScale(self) --> String

        Returns the current y-scale in use. Currently the string
        'log' or 'linear'. If the axes does not exist it returns None.
        '''
        if self.ax:
            return self.ax.get_yscale()
        else:
            return None


    def CopyToClipboard(self, event = None):
        '''CopyToClipboard(self, event) --> None

        Copy the plot to the clipboard.
        '''
        self.canvas.Copy_to_Clipboard(event = event)

    def PrintSetup(self, event = None):
        '''PrintSetup(self) --> None

        Sets up the printer. Creates a dialog box
        '''
        self.fig_printer.pageSetup()

    def PrintPreview(self, event = None):
        '''PrintPreview(self) --> None

        Prints a preview on screen.
        '''
        self.fig_printer.previewFigure(self.figure)

    def Print(self, event= None):
        '''Print(self) --> None

        Print the figure.
        '''
        self.fig_printer.printFigure(self.figure)


    def SetCallbackWindow(self, window):
        '''SetCallbackWindow(self, window) --> None

        Sets the callback window that should recieve the events from
        picking.
        '''

        self.callback_window = window

    def OnLeftDblClick(self, event):
        if self.ax and self.zoom:
            tmp = self.GetAutoScale()
            self.SetAutoScale(True)
            self.AutoScale()
            self.SetAutoScale(tmp)
            #self.AutoScale()
            #self.flush_plot()
            #self.ax.set_autoscale_on(False)

    def OnLeftMouseButtonDown(self, event):
        self.start_pos = event.GetPosition()
        #print 'Left Mouse button pressed ', self.ax.transData.inverse_xy_tup(self.start_pos)
        class Point:
            pass
        p = Point()
        p.x, p.y = self.start_pos
        size = self.canvas.GetClientSize()
        p.y = (size.height - p.y)
        if self.zoom and self.ax:
            if mat_ver > zoom_ver:
                in_axes = self.ax.in_axes(p)
            else:
                in_axes = self.ax.in_axes(*self.start_pos)
            if in_axes:
                self.zooming = True
                self.cur_rect = None
                self.canvas.CaptureMouse()
            else:
                self.zooming = False
        elif self.ax:
            size = self.canvas.GetClientSize()
            if mat_ver > zoom_ver:
                xy = self.ax.transData.inverted().transform(\
                    array([self.start_pos[0], size.height-self.start_pos[1]])\
                    [newaxis,:])
                x, y = xy[0,0], xy[0,1]
            else:
                x, y = self.ax.transData.inverse_xy_tup(\
                    (self.start_pos[0], size.height - self.start_pos[1]))
            if self.callback_window:
                evt = plot_position(text = '(%.3e, %.3e)'%(x, y))
                wx.PostEvent(self.callback_window, evt)
            #print x,y


    def OnMouseMove(self, event):
        if self.zooming and event.Dragging() and event.LeftIsDown():
            self.cur_pos = event.GetPosition()
            #print 'Mouse Move ', self.ax.transData.inverse_xy_tup(self.cur_pos)
            class Point:
                pass
            p = Point()
            p.x, p.y = self.cur_pos
            size = self.canvas.GetClientSize()
            p.y = (size.height - p.y)
            if mat_ver > zoom_ver:
                in_axes = self.ax.in_axes(p)
            else:
                in_axes = self.ax.in_axes(*self.start_pos)
            if in_axes:
                new_rect = (min(self.start_pos[0], self.cur_pos[0]),
                            min(self.start_pos[1], self.cur_pos[1]),
                        abs(self.cur_pos[0] - self.start_pos[0]),
                        abs(self.cur_pos[1] - self.start_pos[1]))
                self._DrawAndErase(new_rect, self.cur_rect)
                self.cur_rect = new_rect
        #event.Skip()

    def OnLeftMouseButtonUp(self, event):
        if self.canvas.HasCapture():
            #print 'Left Mouse button up'
            self.canvas.ReleaseMouse()
            if self.zooming and self.cur_rect:
                # Note: The coordinte system for matplotlib have a different
                # direction of the y-axis and a different origin!
                size = self.canvas.GetClientSize()
                if mat_ver > zoom_ver:
                    start = self.ax.transData.inverted().transform(\
                    array([self.start_pos[0], size.height-self.start_pos[1]])[newaxis,:])
                    end = self.ax.transData.inverted().transform(\
                    array([self.cur_pos[0], size.height-self.cur_pos[1]])[newaxis, :])
                    xend, yend = end[0,0], end[0,1]
                    xstart, ystart = start[0,0], start[0,1]
                else:
                    xstart, ystart = self.ax.transData.inverse_xy_tup(\
                        (self.start_pos[0], size.height-self.start_pos[1]))
                    xend, yend = self.ax.transData.inverse_xy_tup(\
                        (self.cur_pos[0], size.height-self.cur_pos[1]))

                #print xstart, xend
                #print ystart, yend
                self.ax.set_xlim(min(xstart,xend), max(xstart,xend))
                self.ax.set_ylim(min(ystart,yend), max(ystart,yend))
                self.flush_plot()
            self.zooming = False

    def _DrawAndErase(self, box_to_draw, box_to_erase = None):
        '''_DrawAndErase(self, box_to_draw, box_to_erase = None) --> None
        '''
        dc = wx.ClientDC(self.canvas)
        # dc.BeginDrawing()
        dc.SetPen(wx.Pen(wx.WHITE, 1, wx.DOT))
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetLogicalFunction(wx.XOR)
        if box_to_erase:
            dc.DrawRectangle(*box_to_erase)
        dc.DrawRectangle(*box_to_draw)
        # dc.EndDrawing()

    def OnContextMenu(self, event):
        '''OnContextMenu(self, event) --> None

        Callback to show the popmenu for the plot which allows various
        settings to be made.
        '''
        menu = wx.Menu()

        zoomID = wx.NewId()
        menu.AppendCheckItem(zoomID, "Zoom")
        menu.Check(zoomID, self.GetZoom())
        def OnZoom(event):
            self.SetZoom(not self.GetZoom())
        self.Bind(wx.EVT_MENU, OnZoom, id = zoomID)

        zoomallID = wx.NewId()
        menu.Append(zoomallID, 'Zoom All')
        def zoomall(event):
            tmp = self.GetAutoScale()
            self.SetAutoScale(True)
            self.AutoScale()
            self.SetAutoScale(tmp)
            #self.flush_plot()
        self.Bind(wx.EVT_MENU, zoomall, id = zoomallID)

        copyID = wx.NewId()
        menu.Append(copyID, "Copy")
        def copy(event):
            self.CopyToClipboard()
        self.Bind(wx.EVT_MENU, copy, id = copyID)

        yscalemenu = wx.Menu()
        logID = wx.NewId()
        linID = wx.NewId()
        yscalemenu.AppendRadioItem(logID, "log")
        yscalemenu.AppendRadioItem(linID, "linear")
        menu.AppendMenu(-1, "y-scale", yscalemenu)
        if self.GetYScale() == 'log':
            yscalemenu.Check(logID, True)
        else:
            yscalemenu.Check(linID, True)

        def yscale_log(event):
            if self.ax:
                self.SetYScale('log')
                self.AutoScale()
                self.flush_plot()
        def yscale_lin(event):
            if self.ax:
                self.SetYScale('lin')
                self.AutoScale()
                self.flush_plot()
        self.Bind(wx.EVT_MENU, yscale_log, id = logID)
        self.Bind(wx.EVT_MENU, yscale_lin, id = linID)

        autoscaleID = wx.NewId()
        menu.AppendCheckItem(autoscaleID, "Autoscale")
        menu.Check(autoscaleID, self.GetAutoScale())
        def OnAutoScale(event):
            self.SetAutoScale(not self.GetAutoScale())
        self.Bind(wx.EVT_MENU, OnAutoScale, id = autoscaleID)

        # Time to show the menu
        self.PopupMenu(menu)

        menu.Destroy()

    def flush_plot(self):
        #self._SetSize()
        #self.canvas.gui_repaint(drawDC = wx.PaintDC(self))
        #self.ax.set_yscale(self.scale)
        self.canvas.draw()

    def update(self, data):
        pass