2009-06-13 40 views
5

我需要在Delphi 2009中显示格式化的日志。格式不一定要实现说html的所有功能,但是一小部分例如颜色,字体样式等。如何在Delphi中显示格式化(颜色,样式等)日志?

目前我正在使用TRichEdit和我自己的专有标签例如这是蓝色的。由于不能直接访问RTF文本,因此将它与TRichEdit一起使用是非常令人费解的。例如,要颜色文字蓝色我必须:

  1. 解析附加文本提取标签,找出需要格式化和怎么样的文字。
  2. 选择文字。
  3. 应用格式。
  4. 取消选择文本并将选定内容移动到准备进行下一个附加操作的文本末尾。

所有这些都很慢,也很慢。您是否知道使用TRichEdit或另一种更适合该工作的控件可以实现更好(更快)的方式?

我应该提到我已经考虑在TWebBrowser中使用HTML。这种方法的问题是日志的长度可能在1到100000行之间。如果我使用普通的html查看器,我需要每次都设置整个文本,而不是简单地附加它。

此外,日志需要实时更新,因为我添加了行。不是简单地从文件中读取并显示一次。

回答

9

简单的解决方案:使用具有自定义绘制方法的TListBox,并使用仅包含基本信息而不是格式(这将应用于表示代码中)的对象将日志条目放入TObjectList中。使用虚拟字符串列表/ VirtualTreeView组件。只有需要显示的项目才会被渲染,这将节省资源。

+1

Virtual TreeView的+1 – gabr 2009-06-13 10:03:33

+0

这有唯一的缺点,即文本无法选择并复制到剪贴板。 – mghie 2009-06-13 18:21:18

0

我收集你想要显示一个现有的纯文本日志,但应用它的颜色?

这是我能想到的几个选项:

  • 直接编写RTF; AFAIK,TRichEdit确实提供对RTF代码的直接访问;只需将PlainText属性切换为False,然后设置Text字符串属性即可。但是...祝你好运,组装正确的RTF代码。
  • 将您的日志转换为HTML,并使用TWebBrowser控件来显示它。
  • 使用Scintilla(或其他),突出的控制,并推出自己的语法高亮...

如果你正在写日志自己,你也可以使用一个TRichEdit生成的日志中RTF第一个地方。或者,您可以使用HTML或XML格式生成日志(然后使用XSLT将其转换为任何您喜欢的内容)。

+0

不完全是。我想要一个显示并实时滚动的日志,因为我在其上添加了额外的行。 – norgepaul 2009-06-13 09:25:50

4

假设你的日志为100万行代码,你可以使用HTML或RTF,在干净的解决方案(和我处理100-1,000,000)是使用与

Style := lbVirtualOwnerDraw; 
OnDrawItem := ListDrawItem; // your own function (example in help file) 
  1. 忘记(如mjustin建议)一个的TListBox以任何适用于其他应用程序的格式定义数据数组。我用一个简单的LogObject去。
  2. 商店所有的链表类LogObjects,每次有是列表中的变化(添加,删除),调整TListBox.Count以匹配新的链表类计数。
  3. 定义ListDrawItem自己采取的指数,你可以从YOUE链表类的信息(数据库,无论..)和需求分析。

因为您一次只能查看几个条目,所以“按需解析”方法明显更好,因为在您尝试解析所有百万行时,在加载时没有“减速”。

不知道您的实际问题,我只能说,在我的经验是,一旦学习和掌握在大多数面向数据的应用程序非常有用的技术。

增强包括attacheing上述列表框中选择一个标题控制(I在面板一起将它们包装)并且可以创建一个优越的TListView控制。将一些排序逻辑附加到标题控件上的click事件上,你可以对你的对象列表进行排序,你所要做的就是调用ListBox.Invalidate来刷新视图(当它可以)。

++用于实时更新。我现在这样做,是要触发一个计时器事件来调整ListBox.Count,因为您不想每秒更新一次列表框1000次.. :-)

+0

如果超过100000-300000的数量级,我不会使用tstrings/tstringlist。指针列表的重新分配会让你的记忆变成瑞士奶酪。 – 2009-06-13 23:21:29

1

您可能想要购买一个词法扫描仪或Delphi的源代码/语法高亮组件。有很多可用的,大部分都不是很贵。在你的情况下,你会想测试一些,找到一个足够满足你的需求的效率。

有几个例子是:

为了突出显示非常大的日志文件的效率,请查看专门突出显示文本文件的内容。他们应该非常快。但是RichEdit实际上也不是懒散。

1

,如果你决定使用一个TListBox中所建议的,请确保您允许用户复制他们正在查看到剪贴板线的细节。没有比不能从日志中复制行更糟糕的事了。

0

对于那些有兴趣的,这里是我最终使用的代码。如果将它附加到TVirtualStringTree的OnAfterCellPaint事件上,它会给出所需的结果。

(* 
    DrawHTML - Draws text on a canvas using tags based on a simple subset of HTML/CSS 

    <B> - Bold e.g. <B>This is bold</B> 
    <I> - Italic e.g. <I>This is italic</I> 
    <U> - Underline e.g. <U>This is underlined</U> 
    <font-color=x> Font colour e.g. 
       <font-color=clRed>Delphi red</font-color> 
       <font-color=#FFFFFF>Web white</font-color> 
       <font-color=$000000>Hex black</font-color> 
    <font-size=x> Font size e.g. <font-size=30>This is some big text</font-size> 
    <font-family> Font family e.g. <font-family=Arial>This is arial</font-family> 
*) 
procedure TfrmSNMPMIBBrowser.DrawHTML(const ARect: TRect; const ACanvas: TCanvas; const Text: String); 

    function CloseTag(const ATag: String): String; 
    begin 
    Result := concat('/', ATag); 
    end; 

    function GetTagValue(const ATag: String): String; 
    var 
    p: Integer; 
    begin 
    p := pos('=', ATag); 

    if p = 0 then 
     Result := '' 
    else 
     Result := copy(ATag, p + 1, MaxInt); 
    end; 

    function ColorCodeToColor(const Value: String): TColor; 
    var 
    HexValue: String; 
    begin 
    Result := 0; 

    if Value <> '' then 
    begin 
     if (length(Value) >= 2) and (copy(Uppercase(Value), 1, 2) = 'CL') then 
     begin 
     // Delphi colour 
     Result := StringToColor(Value); 
     end else 
     if Value[1] = '#' then 
     begin 
     // Web colour 
     HexValue := copy(Value, 2, 6); 

     Result := RGB(StrToInt('$'+Copy(HexValue, 1, 2)), 
         StrToInt('$'+Copy(HexValue, 3, 2)), 
         StrToInt('$'+Copy(HexValue, 5, 2))); 
     end 
     else 
     // Hex or decimal colour 
     Result := StrToIntDef(Value, 0); 
    end; 
    end; 

const 
    TagBold = 'B'; 
    TagItalic = 'I'; 
    TagUnderline = 'U'; 
    TagBreak = 'BR'; 
    TagFontSize = 'FONT-SIZE'; 
    TagFontFamily = 'FONT-FAMILY'; 
    TagFontColour = 'FONT-COLOR'; 

var 
    x, y, idx, CharWidth, MaxCharHeight: Integer; 
    CurrChar: Char; 
    Tag, TagValue: String; 
    PreviousFontColor: TColor; 
    PreviousFontFamily: String; 
    PreviousFontSize: Integer; 

begin 
    // Start - required if used with TVirtualStringTree 
    ACanvas.Font.Size := Canvas.Font.Size; 
    ACanvas.Font.Name := Canvas.Font.Name; 
    ACanvas.Font.Color := Canvas.Font.Color; 
    ACanvas.Font.Style := Canvas.Font.Style; 
    // End 

    PreviousFontColor := ACanvas.Font.Color; 
    PreviousFontFamily := ACanvas.Font.Name; 
    PreviousFontSize := ACanvas.Font.Size; 

    x := ARect.Left; 
    y := ARect.Top; 
    idx := 1; 

    MaxCharHeight := ACanvas.TextHeight('Ag'); 

    While idx <= length(Text) do 
    begin 
    CurrChar := Text[idx]; 

    // Is this a tag? 
    if CurrChar = '<' then 
    begin 
     Tag := ''; 

     inc(idx); 

     // Find the end of then tag 
     while (Text[idx] <> '>') and (idx <= length(Text)) do 
     begin 
     Tag := concat(Tag, UpperCase(Text[idx])); 

     inc(idx); 
     end; 

     /////////////////////////////////////////////////// 
     // Simple tags 
     /////////////////////////////////////////////////// 
     if Tag = TagBold then 
     ACanvas.Font.Style := ACanvas.Font.Style + [fsBold] else 

     if Tag = TagItalic then 
     ACanvas.Font.Style := ACanvas.Font.Style + [fsItalic] else 

     if Tag = TagUnderline then 
     ACanvas.Font.Style := ACanvas.Font.Style + [fsUnderline] else 

     if Tag = TagBreak then 
     begin 
     x := ARect.Left; 

     inc(y, MaxCharHeight); 
     end else 

     /////////////////////////////////////////////////// 
     // Closing tags 
     /////////////////////////////////////////////////// 
     if Tag = CloseTag(TagBold) then 
     ACanvas.Font.Style := ACanvas.Font.Style - [fsBold] else 

     if Tag = CloseTag(TagItalic) then 
     ACanvas.Font.Style := ACanvas.Font.Style - [fsItalic] else 

     if Tag = CloseTag(TagUnderline) then 
     ACanvas.Font.Style := ACanvas.Font.Style - [fsUnderline] else 

     if Tag = CloseTag(TagFontSize) then 
     ACanvas.Font.Size := PreviousFontSize else 

     if Tag = CloseTag(TagFontFamily) then 
     ACanvas.Font.Name := PreviousFontFamily else 

     if Tag = CloseTag(TagFontColour) then 
     ACanvas.Font.Color := PreviousFontColor else 

     /////////////////////////////////////////////////// 
     // Tags with values 
     /////////////////////////////////////////////////// 
     begin 
     // Get the tag value (everything after '=') 
     TagValue := GetTagValue(Tag); 

     if TagValue <> '' then 
     begin 
      // Remove the value from the tag 
      Tag := copy(Tag, 1, pos('=', Tag) - 1); 

      if Tag = TagFontSize then 
      begin 
      PreviousFontSize := ACanvas.Font.Size; 
      ACanvas.Font.Size := StrToIntDef(TagValue, ACanvas.Font.Size); 
      end else 

      if Tag = TagFontFamily then 
      begin 
      PreviousFontFamily := ACanvas.Font.Name; 
      ACanvas.Font.Name := TagValue; 
      end; 

      if Tag = TagFontColour then 
      begin 
      PreviousFontColor := ACanvas.Font.Color; 
      ACanvas.Font.Color := ColorCodeToColor(TagValue); 
      end; 
     end; 
     end; 
    end 
    else 
    // Draw the character if it's not a ctrl char 
    if CurrChar >= #32 then 
    begin 
     CharWidth := ACanvas.TextWidth(CurrChar); 

     if x + CharWidth > ARect.Right then 
     begin 
     x := ARect.Left; 

     inc(y, MaxCharHeight); 
     end; 

     if y + MaxCharHeight < ARect.Bottom then 
     begin 
     ACanvas.Brush.Style := bsClear; 

     ACanvas.TextOut(x, y, CurrChar); 
     end; 

     x := x + CharWidth; 
    end; 

    inc(idx); 
    end; 
end; 
相关问题