我需要在Delphi 2009中显示格式化的日志。格式化不必实现说html的所有功能,而是一个小的子集,例如颜色,字体样式等。
目前我正在使用TRichEdit和我自己的专有标签,例如这是蓝色的。由于无法直接访问RTF文本,因此将其与TRichEdit配合使用非常复杂。例如,要将文本着色为蓝色,我必须:
所有这些都是黑客和缓慢的。你知道用TRichEdit或其他更适合这项工作的控件来做更好(更快)的方法吗?
我应该提一下,我考虑过在TWebBrowser中使用HTML。这种方法的问题是日志可以是1到100000行的任何地方。如果我使用普通的html查看器,我需要每次都设置整个文本而不是简单地附加它。
此外,当我向其添加行时,需要实时更新日志。不只是从文件中读取并显示一次。
答案 0 :(得分:9)
简单解决方案:使用带有自定义绘制方法的TListBox,并使用仅包含基本信息的对象将日志条目放在TObjectList中,而不是格式化(这将在演示代码中应用)。
或使用虚拟字符串列表/ VirtualTreeView组件。只会渲染需要显示的项目,这样可以节省资源。
答案 1 :(得分:4)
假设您的日志长度为1,000,000行,您可以忘记使用HTML或RTF,最干净的解决方案(我处理100-1,000,000)将使用(如mjustin建议的)TListBox
Style := lbVirtualOwnerDraw;
OnDrawItem := ListDrawItem; // your own function (example in help file)
因为您一次只能查看几个条目,所以“按需解析”方法明显更好,因为在您尝试解析所有百万行时,加载时没有“减速”。
不知道你的实际问题我可以说,根据我的经验,这是一种技术,一旦学习和掌握,在大多数面向数据的应用程序中都很有用。
增强功能包括在列表框上方附加标题控件(我将它们包装在一个面板中),您可以创建一个优秀的TListView控件。将一些排序逻辑附加到标题控件上的click事件,您可以对对象列表进行排序,所有您需要做的就是调用ListBox.Invalidate来刷新视图(如果可以的话)。
++用于实时更新。我现在这样做,就是触发一个计时器事件来调整ListBox.Count,因为你不想每秒更新列表框1000次......: - )
答案 2 :(得分:1)
您可能想为Delphi购买词法扫描程序或源代码/语法荧光笔组件。有许多可用,大多数都不是很昂贵。在您的情况下,您需要测试一些并找到一个足以满足您需求的效率。
一些例子是:
Syntax Highlighting components - 列出SynEdit (open source),Scintilla,Tsyncontrol和其他一些
为了突出显示非常大的日志文件,请查看专门突出显示文本文件的文件。他们应该非常快。但是RichEdit也没有懈怠。
答案 3 :(得分:1)
如果您决定按照建议使用TListbox,请确保您允许用户将他们正在查看的行的详细信息复制到剪贴板。没有什么比不能从日志中复制行更糟糕了。
答案 4 :(得分:0)
我收集你想要显示现有的纯文本日志,但是应用颜色吗?
以下是我能想到的几个选项:
如果您自己编写日志,您也可以使用TRichEdit首先在RTF中生成日志。或者您可以使用HTML或XML生成日志(然后可以使用XSLT将其转换为您喜欢的任何内容)。
答案 5 :(得分: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;