你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 图形图象处理与游戏编程
5.16 用Delphi实现可视化答题卡设计器(中)
 

end;

 

可以看到,THorizontalLeftSizeBlockTHorizontalRightSizeBlock都是从TSizeBlock继承的,并分别实现了SetNewPositionSizeBlockMouseMove方法。这两个方法的实现代码如下:

// THorizontalLeftSizeBlock类的实现代码

procedure THorizontalLeftSizeBlock.SetNewPosition;

begin

  // 设置左尺寸块在纵向的中间位置

    Top := MoveWinControl.Top + MoveWinControl.Height div 2 - Height div 2;

    Left := MoveWinControl.Left - Width div 2;  // 设置左尺寸块在横向的中间位置

end;

 

procedure THorizontalLeftSizeBlock.SizeBlockMouseMove(Sender: TObject;

  Shift: TShiftState; X, Y: Integer);

begin

    inherited;

    if MoveFlag = false then exit; 

  if MoveWinControl.Width + OldX - X <= MoveWinControl.MinWidth then

  begin

    exit;

    end;

    MoveWinControl.BlockName := [bnLeft];   // 设置当前移动的是左尺寸块

    MoveWinControl.Width := MoveWinControl.Width + OldX - X;

  self.Left := self.Left + X - OldX; 

    MoveWinControl.Left := MoveWinControl.Left + X - OldX;  // 向左移动相应的位置

end;
  // THorizontalRightSizeBlock
类的实现代码

  procedure THorizontalRightSizeBlock.SetNewPosition;

  begin

  // 设置右尺寸块在纵向的中间位置

    Top := MoveWinControl.Top + MoveWinControl.Height div 2 - Height div 2;

// 设置右尺寸块在横向的中间位置

    Left := MoveWinControl.Left + MoveWinControl.Width - Width div 2;

  end;

  procedure THorizontalRightSizeBlock.SizeBlockMouseMove(Sender: TObject;

    Shift: TShiftState; X, Y: Integer);

  begin

    inherited;

    if MoveFlag = false then exit;

    if MoveWinControl.Width + X - OldX <= MoveWinControl.MinWidth then

    begin

      exit;

    end;

    MoveWinControl.BlockName := [bnRight];   // 设置当前移动的是右尺寸块

    MoveWinControl.Width := MoveWinControl.Width + X - OldX;

    self.Left := self.Left + X - OldX;   // 向右移动相应的位置

  end;

 

       从上面的代码中可以看到,在.SizeBlockMouseMove过程中根据尺寸块的类型来设置图形元素的相应位置,其他移动块的实现和这两个类的实现类似。

 

三、答题卡组件类

 

前面介绍了如何实现答题卡组件类的基类,现在将讨论如何实现具体的组件。首先介绍一个最重要的组件类:准考证类。有很多读者都参与过标准化的考试,在这种考试中所使用的答题卡都会有一个填写准考证的地方。这个部分可以用笔填写阿拉伯数字的准考证号,在下面是0 9的数字。考生需要根据准考证号涂相应的数字。准考证组件如图3所示。由于不管是准考证,还是答题区,都有涂数字或字母的区域(它们的区别只是数字的多少不同,如答题区一般是ABCD四个区域,而准考证号是0910个数字。因此,可以将这个特性提炼出来形成一个类:TInfoCollection。这个类的定义如下:

 

//信息采集区

  TInfoCollection = class(TWidgetContainer)

  private

    movement: integer;

  public

   

    Color_Border: TColor;  // 涂点边框的颜色

    Color_Text: TColor;    // 涂点文字的颜色

    Width_SmallRegion, Height_SmallRegion: integer;

    GridToRegionX, GridToRegionY: integer;

    GridHeight: integer;

    TextSize: integer;    // 涂点文字的大小

    VerticalSpace: integer;

    HorizontalSpace: integer;

    title: string;

    Cols: integer;  // 每个涂点区的列数

    Rows: integer;

    Left_SmallRegion, Top_SmallRegion: integer;

    TextList: array of String;

    TextStyle: integer; // 涂点的类型。0: A-D  1: 0-9  2: 0-9X

    ShowGrid: Boolean;   // 是否显示网格

    GridLineWidth: integer;  // 网络线宽度

  private

    procedure Init; override;

    procedure SetAttr; virtual;

    //画信息采集小方框

    procedure DrawSmallRegion(left, top: integer; text: String);  // 画每一个涂点

    procedure DrawCol(left, top: integer);   // 画一列涂点

    procedure DrawGrid();    // 使用多列涂点组成一个网络

  protected

    procedure DrawAll;  override; //画信息采集区

  public

    // 覆盖TWidgetContainer类的保存、装载和复制当前图形元素的方法

    procedure Save(fn, name: String); override;

    procedure LoadFromFile(ini: TIniFile; section: string); override;

    procedure Copy(var newwidget: TWidgetContainer); override;

  end;

 

    TInfoCollection类中覆盖了TWidgetContainer类的DrawAll过程,用于按行和列画涂点。DrawAll过程的实现代码如下:

  procedure TInfoCollection.DrawAll;

  var

    i: integer;

    c: integer;

    rect: TRect;

  begin

    inherited;

    SetAttr;   // 设置当前答题卡组件的属性

    // 开始化涂点外围的边框

    for i := 1 to Cols do

    begin

      // 设置每一列的坐标

      rect.Left := (i - 1) *(Width_SmallRegion) - HorizontalSpace;

      rect.Top := Top_SmallRegion;

      rect.Right := rect.Left + Width_SmallRegion + 2 * HorizontalSpace;

      rect.Bottom := rect.Top + Rows * Height_SmallRegion;

 

      if i mod 2 = 1 then  // 根据奇偶列来确定绘制颜色

        MyCanvas.Brush.Color := rgb(255, 200, 200)

      else

        MyCanvas.Brush.Color := clWhite;

 

      DrawCol(Left_SmallRegion + (i - 1) *(Width_SmallRegion + HorizontalSpace), Top_SmallRegion);

    end;

    DrawGrid;   // 画当前涂点区的所有的涂点

  end;

 

DrawAll过程中调用了DrawGridDrawCol过程,用来画所有的涂点。实质上,还有一个最核心的过程:DrawSmallRegion,这个过程来完成一个基本涂点的绘制工作。这个方法的实现代码如下:

procedure TInfoCollection.DrawSmallRegion(left, top: integer; text: String);

var

  new_left, new_top: integer;

  text_left: integer;

  textrect: TRect;

  textrect1: TRect;

  abc: integer;

begin

  // 设置画笔的宽度

  MyCanvas.Pen.Width := GridLineWidth; 

  // 设置每个涂点的相应坐标

  new_left := relative_x + left;

  new_top := relative_y + top;

  textrect.Left := new_left;

  textrect.Top := new_top;

  textrect.Right := new_left + Width_SmallRegion;

  textrect.Bottom := new_top + Height_SmallRegion;

  abc := Height_smallregion * 2 div 3;

  textrect1.Left := new_left;

  textrect1.Top := new_top - 2;

  textrect1.Bottom := textrect.Bottom + 2;

  textrect1.Right := textrect.Right;

  MyCanvas.FillRect(textrect1);  // 为当前涂点设置相应的背景色

  // 开始画当前的涂点

  MyCanvas.MoveTo(textrect.Left, textrect.Top);

  MyCanvas.LineTo(textrect.left, textrect.bottom);

  MyCanvas.MoveTo(textrect.Left, textrect.Top);

  MyCanvas.LineTo(textrect.Left + abc, textrect.Top);

  MyCanvas.MoveTo(textrect.Left, textrect.bottom);

  MyCanvas.LineTo(textrect.Left + abc, textrect.bottom);

 

  MyCanvas.MoveTo(textrect.right, textrect.Top);

  MyCanvas.LineTo(textrect.right, textrect.bottom);

  MyCanvas.MoveTo(textrect.right, textrect.Top);

  MyCanvas.LineTo(textrect.right - abc, textrect.Top);

  MyCanvas.MoveTo(textrect.right, textrect.bottom);

  MyCanvas.LineTo(textrect.right - abc, textrect.bottom);

 

  text_left := new_left + (Width_SmallRegion - MyCanvas.TextWidth(text)) div 2;

  MyCanvas.Font.Color := Color_Border;

  textrect.Top := new_top + 1;

 

  MyCanvas.Font.Charset := GB2312_CHARSET;

  // 向每个涂点中写相应的字符,如0 - 9,或A-D

  if TextSize > 12 then

    MyCanvas.TextOut(text_left , new_top - 2, text)

  else

    MyCanvas.TextOut(text_left + 1 , new_top - 1 , text);

end;

    在实现完TInfoCollection后,就可以实现准考证类了,这个类的类名为TZKZ。这个类的定义如下:

  //准考证号

  TZKZ = class(TInfoCollection)

  private

    Top_Title: integer;

  private

    procedure Init; override;

    procedure SetAttr; override;

    procedure DrawTitle;   // 输出准考证区域上方的文本,默认为准考证号

  protected

    procedure DrawAll; override;   // 除了继承TInfoCollection中的DrawAll功能外,还负责画Title

  public

    procedure Save(fn, name: String); override;  // 保存当前准考证区域

  end;

    TZKZ类中的核心过程是DrawTitle,这个过程负责画涂点上方的网络线和文本。DrawTitle过程的实现代码如下:
procedure TZKZ.DrawTitle;

var

  i: integer;

  textrect: TRect;

  width: integer;

  title_height: integer;

begin

  // 设置上方文本的颜色、尺寸、网络线的颜色、尺寸等信息

  MyCanvas.Font.Color := clBlack;

  MyCanvas.Font.Size := TextSize + 2;

  MyCanvas.Pen.Color := Color_Border;

  MyCanvas.Pen.Width := GridLineWidth;

  MyCanvas.Brush.Color := clWhite;

  width := Cols *(Width_SmallRegion + HorizontalSpace);

  title_height := 2 * Height_SmallRegion;

  textrect.Top := relative_y + Top_Title + title_height;

  textrect.Bottom := textrect.Top + Top_SmallRegion - GridToRegionY;

  // 画纵向的网格线

  for i := 0 to Cols do

  begin

    textrect.Left := relative_x + i *(Width_SmallRegion + HorizontalSpace);

    textrect.Right := Width_SmallRegion + HorizontalSpace + textrect.Left;

    MyCanvas.MoveTo(textrect.Left, textrect.Top);

    MyCanvas.LineTo(textrect.Left, textrect.Bottom);

  end;

  // 画横向的网格线

  MyCanvas.MoveTo(relative_x, textrect.Top);

  MyCanvas.LineTo(relative_x + width, textrect.Top);

  MyCanvas.MoveTo(relative_x, relative_y);

  MyCanvas.LineTo(relative_x, textrect.Top);

  MyCanvas.MoveTo(relative_x + width, relative_y);

  MyCanvas.LineTo(relative_x + width, textrect.Top);

  MyCanvas.MoveTo(relative_x, relative_y);

  MyCanvas.LineTo(relative_x + width, relative_y);

  textrect.Top := relative_y + (title_height - MyCanvas.TextHeight(title)) div 2;

  textrect.left := relative_x;

  textrect.Right := relative_x + width;

  textrect.Bottom := relative_y + title_height;

  // 画上方的文本

  DrawText(MyCanvas.Handle, PChar(title), Length(title), textrect, DT_CENTER);

end;

    除了准考证组件,还有一个答题区组件,这个答题区组件的类名为TQuestionRegion。实现方法和TZKZ类似。
   
要想让读卡机或其他读卡设备识别答题卡,需要使用某种机制来定位每一个涂点。本文使用的是一种比较简单的方法:同步头。所谓同步头,就是在答题卡左侧或其他侧面的等距黑色小块,如图4显示了一个横向的同步头。

 


 

4

 

    实现横向同步头的类为TTBT_Horizontal,这个类继承于一同步头的基类TTBT。这两个类的定义如下:

TTBT类的定义:

  //同步头蕨类

  TTBT = class(TWidgetContainer)

  protected

    TBT_Count: integer;

  public

    TBT_Height: integer;

    TBT_Vertical_Space: integer;

    TBT_Number: integer;

  private

    procedure Init; override;

  public

    procedure Save(fn, name: String); override;  // 保存当前同步头

    procedure LoadFromFile(ini: TIniFile; section: string); override;  // 装载被保存的同步头

  end;

TTBT_Horizontal类的定义:

  TTBT_Horizontal = class(TTBT)  // 横向同步头

  private

    procedure DrawSingleTBT(left: integer); //画一个同步头

  protected

    procedure DrawAll;  override; //画所有的同步头

  public

    procedure Save(fn, name: String); override;

  end;

    TTBT_Horizontal类中,核心的过程为DrawSingleTBTDrawAll,这两个过程分别用于画单独的小黑块和整个同步头。这两个过程的实现代码如下:
procedure TTBT_Horizontal.DrawSingleTBT(left: integer);

var

  r: TRect;

begin

  inherited;

  r.Left := left + relative_x;

  r.Top := relative_y;

  r.Right := left + TBT_Height + relative_x;

  r.Bottom := bg.Height + relative_y;

  MyCanvas.Brush.Color := clBlack;  // 设置同步头颜色为黑色

  MyCanvas.Pen.Color := clBlack;  // 设置同步头边框颜色为黑色

  MyCanvas.Rectangle(r);  // 画黑色框

  MyCanvas.FillRect(r);   // 用黑色填充同步头

end;

procedure TTBT_Horizontal.DrawAll;

var

  n: integer;  //同步头数

  h: integer;

begin

  n := 0;

  h := TBT_Height;

// 根据同步头的长度计算同步头数,并使用DrawSingleTBT过程画每一个同步头

  while h < bg.Width do

  begin

    inc(n);

    DrawSingleTBT(h - TBT_Height);

    h := h + TBT_Vertical_Space + TBT_Height;

  end;

  TBT_Count := n;   // 记录同步头数

end;

由于答题卡设计器中的组件比较多,因此,在文章中不再一一阐述。

  推荐精品文章

·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089