controls/LogViewer.py
changeset 1784 64beb9e9c749
parent 1768 691083b5682a
child 1831 56b48961cc68
equal deleted inserted replaced
1729:31e63e25b4cc 1784:64beb9e9c749
    35 from util.BitmapLibrary import GetBitmap
    35 from util.BitmapLibrary import GetBitmap
    36 from weakref import proxy
    36 from weakref import proxy
    37 
    37 
    38 THUMB_SIZE_RATIO = 1. / 8.
    38 THUMB_SIZE_RATIO = 1. / 8.
    39 
    39 
       
    40 
    40 def ArrowPoints(direction, width, height, xoffset, yoffset):
    41 def ArrowPoints(direction, width, height, xoffset, yoffset):
    41     if direction == wx.TOP:
    42     if direction == wx.TOP:
    42         return [wx.Point(xoffset + 1, yoffset + height - 2),
    43         return [wx.Point(xoffset + 1, yoffset + height - 2),
    43                 wx.Point(xoffset + width / 2, yoffset + 1),
    44                 wx.Point(xoffset + width / 2, yoffset + 1),
    44                 wx.Point(xoffset + width - 1, yoffset + height - 2)]
    45                 wx.Point(xoffset + width - 1, yoffset + height - 2)]
    45     else:
    46     else:
    46         return [wx.Point(xoffset + 1, yoffset - height + 1),
    47         return [wx.Point(xoffset + 1, yoffset - height + 1),
    47                 wx.Point(xoffset + width / 2, yoffset - 2),
    48                 wx.Point(xoffset + width / 2, yoffset - 2),
    48                 wx.Point(xoffset + width - 1, yoffset - height + 1)]
    49                 wx.Point(xoffset + width - 1, yoffset - height + 1)]
    49 
    50 
       
    51 
    50 class LogScrollBar(wx.Panel):
    52 class LogScrollBar(wx.Panel):
    51 
    53 
    52     def __init__(self, parent, size):
    54     def __init__(self, parent, size):
    53         wx.Panel.__init__(self, parent, size=size)
    55         wx.Panel.__init__(self, parent, size=size)
    54         self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
    56         self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
    56         self.Bind(wx.EVT_MOTION, self.OnMotion)
    58         self.Bind(wx.EVT_MOTION, self.OnMotion)
    57         self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
    59         self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
    58         self.Bind(wx.EVT_PAINT, self.OnPaint)
    60         self.Bind(wx.EVT_PAINT, self.OnPaint)
    59         self.Bind(wx.EVT_SIZE, self.OnResize)
    61         self.Bind(wx.EVT_SIZE, self.OnResize)
    60 
    62 
    61         self.ThumbPosition = 0. # -1 <= ThumbPosition <= 1
    63         self.ThumbPosition = 0.  # -1 <= ThumbPosition <= 1
    62         self.ThumbScrollingStartPos = None
    64         self.ThumbScrollingStartPos = None
    63 
    65 
    64     def GetRangeRect(self):
    66     def GetRangeRect(self):
    65         width, height = self.GetClientSize()
    67         width, height = self.GetClientSize()
    66         return wx.Rect(0, width, width, height - 2 * width)
    68         return wx.Rect(0, width, width, height - 2 * width)
   175                          thumb_rect.width, thumb_rect.height)
   177                          thumb_rect.width, thumb_rect.height)
   176 
   178 
   177         dc.EndDrawing()
   179         dc.EndDrawing()
   178         event.Skip()
   180         event.Skip()
   179 
   181 
       
   182 
   180 BUTTON_SIZE = (70, 15)
   183 BUTTON_SIZE = (70, 15)
       
   184 
   181 
   185 
   182 class LogButton():
   186 class LogButton():
   183 
   187 
   184     def __init__(self, label, callback):
   188     def __init__(self, label, callback):
   185         self.Position = wx.Point(0, 0)
   189         self.Position = wx.Point(0, 0)
   215         dc.DrawRectangle(self.Position.x, self.Position.y,
   219         dc.DrawRectangle(self.Position.x, self.Position.y,
   216                          self.Size.width, self.Size.height)
   220                          self.Size.width, self.Size.height)
   217 
   221 
   218         w, h = dc.GetTextExtent(self.Label)
   222         w, h = dc.GetTextExtent(self.Label)
   219         dc.DrawText(self.Label,
   223         dc.DrawText(self.Label,
   220             self.Position.x + (self.Size.width - w) / 2,
   224                     self.Position.x + (self.Size.width - w) / 2,
   221             self.Position.y + (self.Size.height - h) / 2)
   225                     self.Position.y + (self.Size.height - h) / 2)
       
   226 
   222 
   227 
   223 DATE_INFO_SIZE = 10
   228 DATE_INFO_SIZE = 10
   224 MESSAGE_INFO_SIZE = 18
   229 MESSAGE_INFO_SIZE = 18
       
   230 
   225 
   231 
   226 class LogMessage:
   232 class LogMessage:
   227 
   233 
   228     def __init__(self, tv_sec, tv_nsec, level, level_bitmap, msg):
   234     def __init__(self, tv_sec, tv_nsec, level, level_bitmap, msg):
   229         self.Date = datetime.utcfromtimestamp(tv_sec)
   235         self.Date = datetime.utcfromtimestamp(tv_sec)
   269     def GetHeight(self, draw_date):
   275     def GetHeight(self, draw_date):
   270         if draw_date:
   276         if draw_date:
   271             return DATE_INFO_SIZE + MESSAGE_INFO_SIZE
   277             return DATE_INFO_SIZE + MESSAGE_INFO_SIZE
   272         return MESSAGE_INFO_SIZE
   278         return MESSAGE_INFO_SIZE
   273 
   279 
       
   280 
   274 SECOND = 1
   281 SECOND = 1
   275 MINUTE = 60 * SECOND
   282 MINUTE = 60 * SECOND
   276 HOUR = 60 * MINUTE
   283 HOUR = 60 * MINUTE
   277 DAY = 24 * HOUR
   284 DAY = 24 * HOUR
   278 
   285 
   279 CHANGE_TIMESTAMP_BUTTONS = [(_("1d"), DAY),
   286 CHANGE_TIMESTAMP_BUTTONS = [(_("1d"), DAY),
   280                             (_("1h"), HOUR),
   287                             (_("1h"), HOUR),
   281                             (_("1m"), MINUTE),
   288                             (_("1m"), MINUTE),
   282                             (_("1s"), SECOND)]
   289                             (_("1s"), SECOND)]
   283 
   290 
       
   291 
   284 class LogViewer(DebugViewer, wx.Panel):
   292 class LogViewer(DebugViewer, wx.Panel):
   285 
   293 
   286     def __init__(self, parent, window):
   294     def __init__(self, parent, window):
   287         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
   295         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
   288         DebugViewer.__init__(self, None, False, False)
   296         DebugViewer.__init__(self, None, False, False)
   289 
   297 
   290         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
   298         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
   291         main_sizer.AddGrowableCol(0)
   299         main_sizer.AddGrowableCol(0)
   292         main_sizer.AddGrowableRow(1)
   300         main_sizer.AddGrowableRow(1)
   293 
   301 
   294         filter_sizer = wx.BoxSizer(wx.HORIZONTAL)
   302         filter_sizer = wx.BoxSizer(wx.HORIZONTAL)
   295         main_sizer.AddSizer(filter_sizer, border=5, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW)
   303         main_sizer.AddSizer(filter_sizer, border=5, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW)
   296 
   304 
   297         self.MessageFilter = wx.ComboBox(self, style=wx.CB_READONLY)
   305         self.MessageFilter = wx.ComboBox(self, style=wx.CB_READONLY)
   298         self.MessageFilter.Append(_("All"))
   306         self.MessageFilter.Append(_("All"))
   299         levels = LogLevels[:3]
   307         levels = LogLevels[:3]
   300         levels.reverse()
   308         levels.reverse()
   301         for level in levels:
   309         for level in levels:
   302             self.MessageFilter.Append(_(level))
   310             self.MessageFilter.Append(_(level))
   303         self.Bind(wx.EVT_COMBOBOX, self.OnMessageFilterChanged, self.MessageFilter)
   311         self.Bind(wx.EVT_COMBOBOX, self.OnMessageFilterChanged, self.MessageFilter)
   304         filter_sizer.AddWindow(self.MessageFilter, 1, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
   312         filter_sizer.AddWindow(self.MessageFilter, 1, border=5, flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL)
   305 
   313 
   306         self.SearchMessage = wx.SearchCtrl(self, style=wx.TE_PROCESS_ENTER)
   314         self.SearchMessage = wx.SearchCtrl(self, style=wx.TE_PROCESS_ENTER)
   307         self.SearchMessage.ShowSearchButton(True)
   315         self.SearchMessage.ShowSearchButton(True)
   308         self.SearchMessage.ShowCancelButton(True)
   316         self.SearchMessage.ShowCancelButton(True)
   309         self.Bind(wx.EVT_TEXT_ENTER, self.OnSearchMessageChanged, self.SearchMessage)
   317         self.Bind(wx.EVT_TEXT_ENTER, self.OnSearchMessageChanged, self.SearchMessage)
   310         self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN,
   318         self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN,
   311               self.OnSearchMessageSearchButtonClick, self.SearchMessage)
   319                   self.OnSearchMessageSearchButtonClick, self.SearchMessage)
   312         self.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN,
   320         self.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN,
   313               self.OnSearchMessageCancelButtonClick, self.SearchMessage)
   321                   self.OnSearchMessageCancelButtonClick, self.SearchMessage)
   314         filter_sizer.AddWindow(self.SearchMessage, 3, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
   322         filter_sizer.AddWindow(self.SearchMessage, 3, border=5, flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL)
   315 
   323 
   316         self.CleanButton = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap("Clean"),
   324         self.CleanButton = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap("Clean"),
   317               size=wx.Size(28, 28), style=wx.NO_BORDER)
   325                                                           size=wx.Size(28, 28), style=wx.NO_BORDER)
   318         self.CleanButton.SetToolTipString(_("Clean log messages"))
   326         self.CleanButton.SetToolTipString(_("Clean log messages"))
   319         self.Bind(wx.EVT_BUTTON, self.OnCleanButton, self.CleanButton)
   327         self.Bind(wx.EVT_BUTTON, self.OnCleanButton, self.CleanButton)
   320         filter_sizer.AddWindow(self.CleanButton)
   328         filter_sizer.AddWindow(self.CleanButton)
   321 
   329 
   322         message_panel_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
   330         message_panel_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
   323         message_panel_sizer.AddGrowableCol(0)
   331         message_panel_sizer.AddGrowableCol(0)
   324         message_panel_sizer.AddGrowableRow(0)
   332         message_panel_sizer.AddGrowableRow(0)
   325         main_sizer.AddSizer(message_panel_sizer, border=5, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW)
   333         main_sizer.AddSizer(message_panel_sizer, border=5, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW)
   326 
   334 
   327         self.MessagePanel = wx.Panel(self)
   335         self.MessagePanel = wx.Panel(self)
   328         if wx.Platform == '__WXMSW__':
   336         if wx.Platform == '__WXMSW__':
   329             self.Font = wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName='Courier New')
   337             self.Font = wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName='Courier New')
   330         else:
   338         else:
   401                 return LogMessage(tv_sec, tv_nsec, level, self.LevelIcons[level], msg)
   409                 return LogMessage(tv_sec, tv_nsec, level, self.LevelIcons[level], msg)
   402         return None
   410         return None
   403 
   411 
   404     def ResetLogCounters(self):
   412     def ResetLogCounters(self):
   405         self.previous_log_count = [None]*LogLevelsCount
   413         self.previous_log_count = [None]*LogLevelsCount
   406     
   414 
   407     def SetLogCounters(self, log_count):
   415     def SetLogCounters(self, log_count):
   408         new_messages = []
   416         new_messages = []
   409         for level, count, prev in zip(xrange(LogLevelsCount), log_count, self.previous_log_count):
   417         for level, count, prev in zip(xrange(LogLevelsCount), log_count, self.previous_log_count):
   410             if count is not None and prev != count:
   418             if count is not None and prev != count:
   411                 if prev is None:
   419                 if prev is None:
   412                     dump_end = max(-1, count - 10)
   420                     dump_end = max(-1, count - 10)
   413                     oldest_message = (-1, None)
   421                     oldest_message = (-1, None)
   414                 else:
   422                 else:
   415                     dump_end = prev - 1
   423                     dump_end = prev - 1
   416                 for msgidx in xrange(count-1, dump_end,-1):
   424                 for msgidx in xrange(count-1, dump_end, -1):
   417                     new_message = self.GetLogMessageFromSource(msgidx, level)
   425                     new_message = self.GetLogMessageFromSource(msgidx, level)
   418                     if new_message is None:
   426                     if new_message is None:
   419                         if prev is None:
   427                         if prev is None:
   420                             oldest_message = (-1, None)
   428                             oldest_message = (-1, None)
   421                         break
   429                         break
   551         dc.EndDrawing()
   559         dc.EndDrawing()
   552 
   560 
   553         self.MessageScrollBar.RefreshThumbPosition()
   561         self.MessageScrollBar.RefreshThumbPosition()
   554 
   562 
   555     def IsPLCLogEmpty(self):
   563     def IsPLCLogEmpty(self):
   556         empty=True
   564         empty = True
   557         for level, prev in zip(xrange(LogLevelsCount), self.previous_log_count):
   565         for level, prev in zip(xrange(LogLevelsCount), self.previous_log_count):
   558             if prev is not None:
   566             if prev is not None:
   559                 empty=False
   567                 empty = False
   560                 break
   568                 break
   561         return empty
   569         return empty
   562         
   570 
   563     def IsMessagePanelTop(self, message_idx=None):
   571     def IsMessagePanelTop(self, message_idx=None):
   564         if message_idx is None:
   572         if message_idx is None:
   565             message_idx = self.CurrentMessage
   573             message_idx = self.CurrentMessage
   566         if message_idx is not None:
   574         if message_idx is not None:
   567             return self.GetNextMessage(message_idx)[0] is None
   575             return self.GetNextMessage(message_idx)[0] is None