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) |
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 |