四、答题卡组件工具条
这个工具条上的按钮是设计器所需要的答题卡组件,如图2所示。这个工具条的窗体名为frmTool,窗体类型为fsStayOnTop。在点击每一个组件按钮时,系统都会记录所点的是哪个组件。每一个组件按钮是一个TSpeedButton。在TspeedButton的tag中保存了当前的组件按钮代表哪一个答题卡组件。所有的组件按钮都执行同一段代码。这段代码如下: procedure TfrmTool.SpeedButton1Click(Sender: TObject);
var
widget_id: integer;
begin
ButtonDown(FunGroup1, Sender); // 如果某个按钮被按下,将这个按钮设为按下状态
if drawing_widget <> nil then
begin
drawing_widget.Free;
drawing_widget := nil
end;
widget_id := (Sender as TSpeedButton).Tag; // 获得当前组件类型
if widget_id = 0 then // 设置当前的鼠标指针
frmMain.Card.Cursor := crDefault
else
frmMain.Card.Cursor := crCross;
case widget_id of // drawing_widget为全局变量,保存了当前被选中的答题卡组件
VERTICAL_TBT: // 选中纵向同步头
begin
drawing_widget := TTBT_Vertical.Create(nil) as TWidgetContainer;
end;
HORIZONTAL_TBT: // 选中横向同步头
begin
drawing_widget := TTBT_Horizontal.Create(nil) as TWidgetContainer;
end;
INFOCOLLECTION: // 选中信息采集区
begin
drawing_widget := TInfoCollection .Create(nil) as TWidgetContainer;
end;
QUESTIONREGION: // 选中答题区
begin
drawing_widget := TQuestionRegion .Create(nil) as TWidgetContainer;
end;
ZKZREGION: // 选中准考证
begin
drawing_widget := TZKZ .Create(nil) as TWidgetContainer;
end;
BORDER: // 选中边框
begin
drawing_widget := TBorder .Create(nil) as TWidgetContainer;
end;
BORDEREXTENSION: // 选中扩展边框
begin
drawing_widget := TBorderExtension .Create(nil) as TWidgetContainer;
end;
VERTICAL1_TBT: // 选中只有两个方框的纵向同步头
begin
drawing_widget := TTBT_Vertical1.Create(nil) as TWidgetContainer;
end;
HORIZONTAL1_TBT: // 选中只有两个方框的横向同步头
begin
drawing_widget := TTBT_Horizontal1.Create(nil) as TWidgetContainer;
end;
VERTICAL_LINE: // 选中纵向非答题区
begin
drawing_widget := TVertical_Line.Create(nil) as TWidgetContainer;
end;
VERTICAL_SINGLELINE: // 选中纵向直线
begin
drawing_widget := TVertical_SingleLine.Create(nil) as TWidgetContainer;
end;
HORIZONTAL_SINGLELINE: // 选中横向直线
begin
drawing_widget := THorizontal_SingleLine.Create(nil) as TWidgetContainer;
end;
HORIZONTAL_LINE: // 选中横向非答题区
begin
drawing_widget := THorizontal_Line.Create(nil) as TWidgetContainer;
end;
end;
end;
五、主界面
在设计中将答题卡画在TPaintBox上。在画完组件,鼠标抬起后。将在MouseUp事件中把drawing_widget所指的组件对象放到TPaintBox上。MouseUp事件的代码如下:
procedure TfrmMain.pbCardMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
i: integer;
function Abs_Int(x: integer): integer; // 取整数的绝对值
begin
if x < 0 then
result := -x
else
result := x;
end;
function IsSelected(control: TControl): boolean; // 判断组件是否应被选中
var
NewX, NewY, h, w: integer;
begin
if X < OldX then // 鼠标向左侧画
begin
NewX := X;
w := OldX - X;
end
else // 鼠标向右侧画
begin
NewX := OldX;
w := X - OldX;
end;
if Y < OldY then // 鼠标向上侧画
begin
NewY := Y;
h := OldY - Y;
end
else // 鼠标向下侧画
begin
NewY := OldY;
h := Y - OldY;
end;
// 判断鼠标所画的区域是否包含了control所指的组件对象
if (control.Left + control.Width >= NewX) and (control.Top + control.Height >= NewY) then
begin
if (NewX + w > control.Left) and (NewY + h > control.Top) then
begin
result := true;
exit;
end;
end;
result := false;
end;
begin
drawflag := false;
pbCard.Repaint; // 将画布重画
for i := 0 to Card.ControlCount - 1 do // 根据鼠标画的区域选择答题卡的所有的组件
begin
if Card.Controls[i] is TWidgetContainer then
begin
if IsSelected(Card.Controls[i]) then
(Card.Controls[i] as TWidgetContainer).WidgetStatus := [wsAll];
end;
end;
if drawing_widget <> nil then // 开始画答题卡组件
begin
drawing_widget.Parent := card; // 将组件放到TPaintBox中
drawing_widget.Width := Abs_Int(X - OldX); // 设置组件的宽度
if X < OldX then // 计算控制是向左侧画,还是向右侧画
drawing_widget.Left := X
else
drawing_widget.Left := OldX;
if Y < OldY then // 计算控制是向上画,还是向下画
drawing_widget.Top := Y
else
drawing_widget.Top := OldY;
drawing_widget.Height := Abs_Int(Y - OldY); // 设置组件的高度
drawing_widget.WidgetStatus := [wsAll]; // 将组件设为被选中状态(显示所有的尺寸块)
current_widget := drawing_widget;
drawing_widget := nil;
frmTool.SpeedButton1Click(frmTool.SpeedButton1);
changed; // 通知系统当前的答题卡已经被改变
end;
end;
答题卡设计器不仅可以在TPaintBox上设计答题卡,而且还可以根据所设计的答题卡生成一个bmp图。生成bmp图的代码如下: procedure TfrmMain.SaveCard(fn: string);
var
bmp: TBitmap;
i: integer;
rect, rect1: TRect;
wc: TWidgetContainer;
OldCanvas: TCanvas;
begin
bmp := TBitmap.Create; // 建立一个Tbitmap对象
bmp.Width := card.Width; // 将这个Tbitmap对象的宽度设为TPaintBox的宽度
bmp.Height := card.Height; // 将这个Tbitmap对象的高度设为TPaintBox的高度
for i := 0 to card.ControlCount - 1 do // 扫描打题卡中的所有组件,将它们依次画到bmp图上
begin
if card.Controls[i] is TWidgetContainer then // 当TPaintBox中的控制是TwidgetContainer时进行
begin
wc := card.Controls[i] as TWidgetContainer;
rect.Left := wc.Left;
rect.Top := wc.Top;
rect.Right := wc.Width;
rect.Bottom := wc.Height;
rect1.Left := 0;
rect1.Top := 0;
rect1.Right := wc.Width;
rect1.Bottom := wc.Height;
wc.DrawWidgetToCanvas(bmp.Canvas); // 开始在bmp上画相应的答题卡组件
end;
end;
bmp.SaveToFile(fn); // 保存bmp图
bmp.Free;
end;
六、总结
在本文中给出了实现答题卡设计器的核心代码。感兴趣的读者可以根据这些源代码来增加程序的功能,从而设计出更为丰富的答题卡设计器来。
|