controls/DebugVariablePanel/DebugVariableGraphicViewer.py
changeset 1212 b351d3a7917c
parent 1209 953a8f14040a
child 1214 2ef048b5383c
equal deleted inserted replaced
1211:e3f805ba74ca 1212:b351d3a7917c
   296             # Return to parallel graph if there is only one item
   296             # Return to parallel graph if there is only one item
   297             # especially if it's actually orthogonal
   297             # especially if it's actually orthogonal
   298             if len(self.Items) == 1:
   298             if len(self.Items) == 1:
   299                 self.GraphType = GRAPH_PARALLEL
   299                 self.GraphType = GRAPH_PARALLEL
   300             self.ResetGraphics()
   300             self.ResetGraphics()
       
   301     
       
   302     def SetCursorTick(self, cursor_tick):
       
   303         """
       
   304         Set cursor tick
       
   305         @param cursor_tick: Cursor tick
       
   306         """
       
   307         self.CursorTick = cursor_tick
   301     
   308     
   302     def SubscribeAllDataConsumers(self):
   309     def SubscribeAllDataConsumers(self):
   303         """
   310         """
   304         Function that unsubscribe and remove every item that store values of
   311         Function that unsubscribe and remove every item that store values of
   305         a variable that doesn't exist in PLC anymore
   312         a variable that doesn't exist in PLC anymore
   477                      self.ContextualButtonsItem)
   484                      self.ContextualButtonsItem)
   478         # Close contextual menu
   485         # Close contextual menu
   479         self.DismissContextualButtons()
   486         self.DismissContextualButtons()
   480     
   487     
   481     def HandleCursorMove(self, event):
   488     def HandleCursorMove(self, event):
       
   489         """
       
   490         Update Cursor position according to mouse position and graph type
       
   491         @param event: Mouse event
       
   492         """
   482         start_tick, end_tick = self.ParentWindow.GetRange()
   493         start_tick, end_tick = self.ParentWindow.GetRange()
   483         cursor_tick = None
   494         cursor_tick = None
   484         items = self.ItemsDict.values()
   495         items = self.ItemsDict.values()
       
   496         
       
   497         # Graph is orthogonal
   485         if self.GraphType == GRAPH_ORTHOGONAL:
   498         if self.GraphType == GRAPH_ORTHOGONAL:
       
   499             # Extract items data displayed in canvas figure
       
   500             min_start_tick = max(start_tick, self.GetItemsMinCommonTick())
       
   501             start_tick = max(start_tick, min_start_tick)
       
   502             end_tick = max(end_tick, min_start_tick)
   486             x_data = items[0].GetData(start_tick, end_tick)
   503             x_data = items[0].GetData(start_tick, end_tick)
   487             y_data = items[1].GetData(start_tick, end_tick)
   504             y_data = items[1].GetData(start_tick, end_tick)
       
   505             
       
   506             # Search for the nearest point from mouse position
   488             if len(x_data) > 0 and len(y_data) > 0:
   507             if len(x_data) > 0 and len(y_data) > 0:
   489                 length = min(len(x_data), len(y_data))
   508                 length = min(len(x_data), len(y_data)) 
   490                 d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + (y_data[:length,1]-event.ydata) ** 2)
   509                 d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + \
       
   510                                (y_data[:length,1]-event.ydata) ** 2)
       
   511                 
       
   512                 # Set cursor tick to the tick of this point
   491                 cursor_tick = x_data[numpy.argmin(d), 0]
   513                 cursor_tick = x_data[numpy.argmin(d), 0]
       
   514         
       
   515         # Graph is parallel
   492         else:
   516         else:
       
   517             # Extract items tick
   493             data = items[0].GetData(start_tick, end_tick)
   518             data = items[0].GetData(start_tick, end_tick)
       
   519             
       
   520             # Search for point that tick is the nearest from mouse X position
       
   521             # and set cursor tick to the tick of this point
   494             if len(data) > 0:
   522             if len(data) > 0:
   495                 cursor_tick = data[numpy.argmin(numpy.abs(data[:,0] - event.xdata)), 0]
   523                 cursor_tick = data[numpy.argmin(
       
   524                         numpy.abs(data[:,0] - event.xdata)), 0]
       
   525         
       
   526         # Update cursor tick
   496         if cursor_tick is not None:
   527         if cursor_tick is not None:
   497             self.ParentWindow.SetCursorTick(cursor_tick)
   528             self.ParentWindow.SetCursorTick(cursor_tick)
   498     
   529     
   499     def OnCanvasButtonPressed(self, event):
   530     def OnCanvasButtonPressed(self, event):
   500         """
   531         """
   501         Function called when a button of mouse is pressed
   532         Function called when a button of mouse is pressed
   502         @param event: Mouse event
   533         @param event: Mouse event
   503         """
   534         """
   504         # Get mouse position, graph coordinates are inverted comparing to wx
   535         # Get mouse position, graph Y coordinate is inverted in matplotlib
   505         # coordinates
   536         # comparing to wx
   506         width, height = self.GetSize()
   537         width, height = self.GetSize()
   507         x, y = event.x, height - event.y
   538         x, y = event.x, height - event.y
   508         
   539         
   509         # Return immediately if mouse is over a button
   540         # Return immediately if mouse is over a button
   510         if self.IsOverButton(x, y):
   541         if self.IsOverButton(x, y):
   719             self.ParentWindow.ChangeRange(int(-event.step) / 3, tick)
   750             self.ParentWindow.ChangeRange(int(-event.step) / 3, tick)
   720             
   751             
   721             # Vetoing event to prevent parent panel to be scrolled
   752             # Vetoing event to prevent parent panel to be scrolled
   722             self.ParentWindow.VetoScrollEvent = True
   753             self.ParentWindow.VetoScrollEvent = True
   723     
   754     
   724     def OnAxesMotion(self, event):
       
   725         """
       
   726         Function overriding default function called when mouse is dragged for
       
   727         rotating graph preventing refresh to be called too quickly
       
   728         @param event: Mouse event
       
   729         """
       
   730         if self.Is3DCanvas():
       
   731             # Call default function at most 10 times per second
       
   732             current_time = gettime()
       
   733             if current_time - self.LastMotionTime > REFRESH_PERIOD:
       
   734                 self.LastMotionTime = current_time
       
   735                 Axes3D._on_move(self.Axes, event)
       
   736     
       
   737     # Cursor tick move for each arrow key
   755     # Cursor tick move for each arrow key
   738     KEY_CURSOR_INCREMENT = {
   756     KEY_CURSOR_INCREMENT = {
   739         wx.WXK_LEFT: -1,
   757         wx.WXK_LEFT: -1,
   740         wx.WXK_RIGHT: 1,
   758         wx.WXK_RIGHT: 1,
   741         wx.WXK_UP: -10,
   759         wx.WXK_UP: -10,
   764             self.SetCursor(wx.NullCursor)
   782             self.SetCursor(wx.NullCursor)
   765             DebugVariableViewer.OnLeave(self, event)
   783             DebugVariableViewer.OnLeave(self, event)
   766         else:
   784         else:
   767             event.Skip()
   785             event.Skip()
   768     
   786     
       
   787     def GetCanvasMinSize(self):
       
   788         """
       
   789         Return the minimum size of Viewer so that all items label can be
       
   790         displayed
       
   791         @return: wx.Size containing Viewer minimum size
       
   792         """
       
   793         # The minimum height take in account the height of all items, padding
       
   794         # inside figure and border around figure
       
   795         return wx.Size(200, 
       
   796             CANVAS_BORDER[0] + CANVAS_BORDER[1] + 
       
   797             2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items))
       
   798     
       
   799     def SetCanvasSize(self, width, height):
       
   800         """
       
   801         Set Viewer size checking that it respects Viewer minimum size
       
   802         @param width: Viewer width
       
   803         @param height: Viewer height
       
   804         """
       
   805         height = max(height, self.GetCanvasMinSize()[1])
       
   806         self.SetMinSize(wx.Size(width, height))
       
   807         self.RefreshLabelsPosition(height)
       
   808         self.ParentWindow.RefreshGraphicsSizer()
       
   809         
       
   810     def GetAxesBoundingBox(self, parent_coordinate=False):
       
   811         """
       
   812         Return figure bounding box in wx coordinate
       
   813         @param parent_coordinate: True if use parent coordinate (default False)
       
   814         """
       
   815         # Calculate figure bounding box. Y coordinate is inverted in matplotlib
       
   816         # figure comparing to wx panel
       
   817         width, height = self.GetSize()
       
   818         ax, ay, aw, ah = self.figure.gca().get_position().bounds
       
   819         bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1,
       
   820                        aw * width + 2, ah * height + 1)
       
   821         
       
   822         # If parent_coordinate, add Viewer position in parent
       
   823         if parent_coordinate:
       
   824             xw, yw = self.GetPosition()
       
   825             bbox.x += xw
       
   826             bbox.y += yw
       
   827         
       
   828         return bbox
       
   829     
       
   830     def RefreshHighlight(self, x, y):
       
   831         """
       
   832         Refresh Viewer highlight according to mouse position
       
   833         @param x: X coordinate of mouse pointer
       
   834         @param y: Y coordinate of mouse pointer
       
   835         """
       
   836         width, height = self.GetSize()
       
   837         
       
   838         # Mouse is over Viewer figure and graph is not 3D
       
   839         bbox = self.GetAxesBoundingBox()
       
   840         if bbox.InsideXY(x, y) and not self.Is3DCanvas():
       
   841             rect = wx.Rect(bbox.x, bbox.y, bbox.width / 2, bbox.height)
       
   842             # Mouse is over Viewer left part of figure
       
   843             if rect.InsideXY(x, y):
       
   844                 self.SetHighlight(HIGHLIGHT_LEFT)
       
   845             
       
   846             # Mouse is over Viewer right part of figure
       
   847             else:
       
   848                 self.SetHighlight(HIGHLIGHT_RIGHT)
       
   849         
       
   850         # Mouse is over upper part of Viewer
       
   851         elif y < height / 2:
       
   852             # Viewer is upper one in Debug Variable Panel, show highlight
       
   853             if self.ParentWindow.IsViewerFirst(self):
       
   854                 self.SetHighlight(HIGHLIGHT_BEFORE)
       
   855             
       
   856             # Viewer is not the upper one, show highlight in previous one
       
   857             # It prevents highlight to move when mouse leave one Viewer to
       
   858             # another
       
   859             else:
       
   860                 self.SetHighlight(HIGHLIGHT_NONE)
       
   861                 self.ParentWindow.HighlightPreviousViewer(self)
       
   862         
       
   863         # Mouse is over lower part of Viewer
       
   864         else:
       
   865             self.SetHighlight(HIGHLIGHT_AFTER)
       
   866     
       
   867     def OnAxesMotion(self, event):
       
   868         """
       
   869         Function overriding default function called when mouse is dragged for
       
   870         rotating graph preventing refresh to be called too quickly
       
   871         @param event: Mouse event
       
   872         """
       
   873         if self.Is3DCanvas():
       
   874             # Call default function at most 10 times per second
       
   875             current_time = gettime()
       
   876             if current_time - self.LastMotionTime > REFRESH_PERIOD:
       
   877                 self.LastMotionTime = current_time
       
   878                 Axes3D._on_move(self.Axes, event)
       
   879     
       
   880     def GetAddTextFunction(self):
       
   881         """
       
   882         Return function for adding text in figure according to graph type
       
   883         @return: Function adding text to figure
       
   884         """
       
   885         text_func = (self.Axes.text2D if self.Is3DCanvas() else self.Axes.text)
       
   886         def AddText(*args, **kwargs):
       
   887             args = [0, 0, ""]
       
   888             kwargs["transform"] = self.Axes.transAxes
       
   889             return text_func(*args, **kwargs)
       
   890         return AddText
       
   891     
       
   892     def ResetGraphics(self):
       
   893         """
       
   894         Reset figure and graphical elements displayed in it
       
   895         Called any time list of items or graph type change 
       
   896         """
       
   897         # Clear figure from any axes defined
       
   898         self.Figure.clear()
       
   899         
       
   900         # Add 3D projection if graph is in 3D
       
   901         if self.Is3DCanvas():
       
   902             self.Axes = self.Figure.gca(projection='3d')
       
   903             self.Axes.set_color_cycle(['b'])
       
   904             
       
   905             # Override function to prevent too much refresh when graph is 
       
   906             # rotated
       
   907             self.LastMotionTime = gettime()
       
   908             setattr(self.Axes, "_on_move", self.OnAxesMotion)
       
   909             
       
   910             # Init graph mouse event so that graph can be rotated
       
   911             self.Axes.mouse_init()
       
   912             
       
   913             # Set size of Z axis labels
       
   914             self.Axes.tick_params(axis='z', labelsize='small')
       
   915         
       
   916         else:
       
   917             self.Axes = self.Figure.gca()
       
   918             self.Axes.set_color_cycle(COLOR_CYCLE)
       
   919         
       
   920         # Set size of X and Y axis labels
       
   921         self.Axes.tick_params(axis='x', labelsize='small')
       
   922         self.Axes.tick_params(axis='y', labelsize='small')
       
   923         
       
   924         # Init variables storing graphical elements added to figure
       
   925         self.Plots = []      # List of curves
       
   926         self.VLine = None    # Vertical line for cursor
       
   927         self.HLine = None    # Horizontal line for cursor (only orthogonal 2D)
       
   928         self.AxesLabels = [] # List of items variable path text label
       
   929         self.Labels = []     # List of items text label
       
   930         
       
   931         # Get function to add a text in figure according to graph type 
       
   932         add_text_func = self.GetAddTextFunction()
       
   933         
       
   934         # Graph type is parallel or orthogonal in 3D
       
   935         if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
       
   936             num_item = len(self.Items)
       
   937             for idx in xrange(num_item):
       
   938                 
       
   939                 # Get color from color cycle (black if only one item)
       
   940                 color = ('k' if num_item == 1
       
   941                              else COLOR_CYCLE[idx % len(COLOR_CYCLE)])
       
   942                 
       
   943                 # In 3D graph items variable label are not displayed as text
       
   944                 # in figure, but as axis title
       
   945                 if not self.Is3DCanvas():
       
   946                     # Items variable labels are in figure upper left corner
       
   947                     self.AxesLabels.append(
       
   948                         add_text_func(size='small', color=color,
       
   949                                       verticalalignment='top'))
       
   950                 
       
   951                 # Items variable labels are in figure lower right corner
       
   952                 self.Labels.append(
       
   953                     add_text_func(size='large', color=color, 
       
   954                                   horizontalalignment='right'))
       
   955         
       
   956         # Graph type is orthogonal in 2D
       
   957         else:
       
   958             # X coordinate labels are in figure lower side
       
   959             self.AxesLabels.append(add_text_func(size='small'))
       
   960             self.Labels.append(
       
   961                 add_text_func(size='large',
       
   962                               horizontalalignment='right'))
       
   963             
       
   964             # Y coordinate labels are vertical and in figure left side
       
   965             self.AxesLabels.append(
       
   966                 add_text_func(size='small', rotation='vertical'))
       
   967             self.Labels.append(
       
   968                 add_text_func(size='large', rotation='vertical',
       
   969                               verticalalignment='top'))
       
   970        
       
   971         # Refresh position of labels according to Viewer size
       
   972         width, height = self.GetSize()
       
   973         self.RefreshLabelsPosition(height)
       
   974     
   769     def RefreshLabelsPosition(self, height):
   975     def RefreshLabelsPosition(self, height):
   770         canvas_ratio = 1. / height
   976         """
   771         graph_ratio = 1. / ((1.0 - (CANVAS_BORDER[0] + CANVAS_BORDER[1]) * canvas_ratio) * height)
   977         Function called when mouse leave Viewer
   772         
   978         @param event: wx.MouseEvent
       
   979         """
       
   980         # Figure position like text position in figure are expressed is ratio
       
   981         # canvas size and figure size. As we want that border around figure and
       
   982         # text position in figure don't change when canvas size change, we
       
   983         # expressed border and text position in pixel on screen and apply the
       
   984         # ratio calculated hereafter to get border and text position in
       
   985         # matplotlib coordinate 
       
   986         canvas_ratio = 1. / height # Divide by canvas height in pixel
       
   987         graph_ratio = 1. / (
       
   988             (1.0 - (CANVAS_BORDER[0] + CANVAS_BORDER[1]) * canvas_ratio)
       
   989              * height)             # Divide by figure height in pixel
       
   990         
       
   991         # Update position of figure (keeping up and bottom border the same
       
   992         # size)
   773         self.Figure.subplotpars.update(
   993         self.Figure.subplotpars.update(
   774             top= 1.0 - CANVAS_BORDER[1] * canvas_ratio, 
   994             top= 1.0 - CANVAS_BORDER[1] * canvas_ratio, 
   775             bottom= CANVAS_BORDER[0] * canvas_ratio)
   995             bottom= CANVAS_BORDER[0] * canvas_ratio)
   776         
   996         
       
   997         # Update position of items labels
   777         if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
   998         if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
   778             num_item = len(self.Items)
   999             num_item = len(self.Items)
   779             for idx in xrange(num_item):
  1000             for idx in xrange(num_item):
       
  1001                 
       
  1002                 # In 3D graph items variable label are not displayed
   780                 if not self.Is3DCanvas():
  1003                 if not self.Is3DCanvas():
       
  1004                     # Items variable labels are in figure upper left corner
   781                     self.AxesLabels[idx].set_position(
  1005                     self.AxesLabels[idx].set_position(
   782                         (0.05, 
  1006                         (0.05, 
   783                          1.0 - (CANVAS_PADDING + AXES_LABEL_HEIGHT * idx) * graph_ratio))
  1007                          1.0 - (CANVAS_PADDING + 
       
  1008                                 AXES_LABEL_HEIGHT * idx) * graph_ratio))
       
  1009                 
       
  1010                 # Items variable labels are in figure lower right corner
   784                 self.Labels[idx].set_position(
  1011                 self.Labels[idx].set_position(
   785                     (0.95, 
  1012                     (0.95, 
   786                      CANVAS_PADDING * graph_ratio + 
  1013                      CANVAS_PADDING * graph_ratio + 
   787                      (num_item - idx - 1) * VALUE_LABEL_HEIGHT * graph_ratio))
  1014                      (num_item - idx - 1) * VALUE_LABEL_HEIGHT * graph_ratio))
   788         else:
  1015         else:
   789             self.AxesLabels[0].set_position((0.1, CANVAS_PADDING * graph_ratio))
  1016             # X coordinate labels are in figure lower side
   790             self.Labels[0].set_position((0.95, CANVAS_PADDING * graph_ratio))
  1017             self.AxesLabels[0].set_position(
   791             self.AxesLabels[1].set_position((0.05, 2 * CANVAS_PADDING * graph_ratio))
  1018                     (0.1, CANVAS_PADDING * graph_ratio))
   792             self.Labels[1].set_position((0.05, 1.0 - CANVAS_PADDING * graph_ratio))
  1019             self.Labels[0].set_position(
   793     
  1020                     (0.95, CANVAS_PADDING * graph_ratio))
       
  1021             
       
  1022             # Y coordinate labels are vertical and in figure left side
       
  1023             self.AxesLabels[1].set_position(
       
  1024                     (0.05, 2 * CANVAS_PADDING * graph_ratio))
       
  1025             self.Labels[1].set_position(
       
  1026                     (0.05, 1.0 - CANVAS_PADDING * graph_ratio))
       
  1027         
       
  1028         # Update subplots
   794         self.Figure.subplots_adjust()
  1029         self.Figure.subplots_adjust()
   795     
  1030     
   796     def GetCanvasMinSize(self):
       
   797         return wx.Size(200, 
       
   798                        CANVAS_BORDER[0] + CANVAS_BORDER[1] + 
       
   799                        2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items))
       
   800     
       
   801     def SetCanvasSize(self, width, height):
       
   802         height = max(height, self.GetCanvasMinSize()[1])
       
   803         self.SetMinSize(wx.Size(width, height))
       
   804         self.RefreshLabelsPosition(height)
       
   805         self.ParentWindow.RefreshGraphicsSizer()
       
   806         
       
   807     def GetAxesBoundingBox(self, absolute=False):
       
   808         width, height = self.GetSize()
       
   809         ax, ay, aw, ah = self.figure.gca().get_position().bounds
       
   810         bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1,
       
   811                        aw * width + 2, ah * height + 1)
       
   812         if absolute:
       
   813             xw, yw = self.GetPosition()
       
   814             bbox.x += xw
       
   815             bbox.y += yw
       
   816         return bbox
       
   817     
       
   818     def RefreshHighlight(self, x, y):
       
   819         width, height = self.GetSize()
       
   820         bbox = self.GetAxesBoundingBox()
       
   821         if bbox.InsideXY(x, y) and not self.Is3DCanvas():
       
   822             rect = wx.Rect(bbox.x, bbox.y, bbox.width / 2, bbox.height)
       
   823             if rect.InsideXY(x, y):
       
   824                 self.SetHighlight(HIGHLIGHT_LEFT)
       
   825             else:
       
   826                 self.SetHighlight(HIGHLIGHT_RIGHT)
       
   827         elif y < height / 2:
       
   828             if self.ParentWindow.IsViewerFirst(self):
       
   829                 self.SetHighlight(HIGHLIGHT_BEFORE)
       
   830             else:
       
   831                 self.SetHighlight(HIGHLIGHT_NONE)
       
   832                 self.ParentWindow.HighlightPreviousViewer(self)
       
   833         else:
       
   834             self.SetHighlight(HIGHLIGHT_AFTER)
       
   835     
       
   836     def ResetGraphics(self):
       
   837         self.Figure.clear()
       
   838         if self.Is3DCanvas():
       
   839             self.Axes = self.Figure.gca(projection='3d')
       
   840             self.Axes.set_color_cycle(['b'])
       
   841             self.LastMotionTime = gettime()
       
   842             setattr(self.Axes, "_on_move", self.OnAxesMotion)
       
   843             self.Axes.mouse_init()
       
   844             self.Axes.tick_params(axis='z', labelsize='small')
       
   845         else:
       
   846             self.Axes = self.Figure.gca()
       
   847             self.Axes.set_color_cycle(COLOR_CYCLE)
       
   848         self.Axes.tick_params(axis='x', labelsize='small')
       
   849         self.Axes.tick_params(axis='y', labelsize='small')
       
   850         self.Plots = []
       
   851         self.VLine = None
       
   852         self.HLine = None
       
   853         self.Labels = []
       
   854         self.AxesLabels = []
       
   855         if not self.Is3DCanvas():
       
   856             text_func = self.Axes.text
       
   857         else:
       
   858             text_func = self.Axes.text2D
       
   859         if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
       
   860             num_item = len(self.Items)
       
   861             for idx in xrange(num_item):
       
   862                 if num_item == 1:
       
   863                     color = 'k'
       
   864                 else:
       
   865                     color = COLOR_CYCLE[idx % len(COLOR_CYCLE)]
       
   866                 if not self.Is3DCanvas():
       
   867                     self.AxesLabels.append(
       
   868                         text_func(0, 0, "", size='small',
       
   869                                   verticalalignment='top', 
       
   870                                   color=color,
       
   871                                   transform=self.Axes.transAxes))
       
   872                 self.Labels.append(
       
   873                     text_func(0, 0, "", size='large', 
       
   874                               horizontalalignment='right',
       
   875                               color=color,
       
   876                               transform=self.Axes.transAxes))
       
   877         else:
       
   878             self.AxesLabels.append(
       
   879                 self.Axes.text(0, 0, "", size='small',
       
   880                                transform=self.Axes.transAxes))
       
   881             self.Labels.append(
       
   882                 self.Axes.text(0, 0, "", size='large',
       
   883                                horizontalalignment='right',
       
   884                                transform=self.Axes.transAxes))
       
   885             self.AxesLabels.append(
       
   886                 self.Axes.text(0, 0, "", size='small',
       
   887                                rotation='vertical',
       
   888                                verticalalignment='bottom',
       
   889                                transform=self.Axes.transAxes))
       
   890             self.Labels.append(
       
   891                 self.Axes.text(0, 0, "", size='large',
       
   892                                rotation='vertical',
       
   893                                verticalalignment='top',
       
   894                                transform=self.Axes.transAxes))
       
   895         width, height = self.GetSize()
       
   896         self.RefreshLabelsPosition(height)
       
   897         
       
   898     def SetCursorTick(self, cursor_tick):
       
   899         self.CursorTick = cursor_tick
       
   900         
       
   901     def RefreshViewer(self, refresh_graphics=True):
  1031     def RefreshViewer(self, refresh_graphics=True):
   902         
  1032         
   903         if refresh_graphics:
  1033         if refresh_graphics:
   904             start_tick, end_tick = self.ParentWindow.GetRange()
  1034             start_tick, end_tick = self.ParentWindow.GetRange()
   905             
  1035             
   942                     self.VLine.set_visible(True)
  1072                     self.VLine.set_visible(True)
   943                 else:
  1073                 else:
   944                     if self.VLine is not None:
  1074                     if self.VLine is not None:
   945                         self.VLine.set_visible(False)
  1075                         self.VLine.set_visible(False)
   946             else:
  1076             else:
   947                 min_start_tick = reduce(max, [item.GetData()[0, 0] 
  1077                 min_start_tick = max(start_tick, self.GetItemsMinCommonTick())
   948                                               for item in self.Items
       
   949                                               if len(item.GetData()) > 0], 0)
       
   950                 start_tick = max(start_tick, min_start_tick)
  1078                 start_tick = max(start_tick, min_start_tick)
   951                 end_tick = max(end_tick, min_start_tick)
  1079                 end_tick = max(end_tick, min_start_tick)
   952                 items = self.ItemsDict.values()
  1080                 items = self.ItemsDict.values()
   953                 x_data, x_min, x_max = OrthogonalDataAndRange(items[0], start_tick, end_tick)
  1081                 x_data, x_min, x_max = OrthogonalDataAndRange(items[0], start_tick, end_tick)
   954                 y_data, y_min, y_max = OrthogonalDataAndRange(items[1], start_tick, end_tick)
  1082                 y_data, y_min, y_max = OrthogonalDataAndRange(items[1], start_tick, end_tick)