本站首页    管理页面    写新日志    退出


«August 2025»
12
3456789
10111213141516
17181920212223
24252627282930
31


公告
暂无公告...

我的分类(专题)

日志更新

最新评论

留言板

链接

我的博客

Blog信息
blog名称:fermos_blog
日志总数:73
评论数量:261
留言数量:30
访问次数:396563
建立时间:2005年3月27日




Visual FoxPro9.0中扩展报表系统功能
软件技术

fermos的博客 发表于 2005/9/8 8:10:27

Visual FoxPro9.0中扩展报表系统功能 http://www.pcdog.com 2004-12-7 天极网 下一页 1 2 3 
 微软在2004年岁末发布最新版Visual Foxpro 9.0,这则消息让我们回想起曾经风光无限的Foxpro,现在它已经被Java、Delphi、VB等众多明星遮掩了光芒,有人认为如果不是诞生在豪门微软,它肯定已经消失。但了解Visual Foxpro的都知道,Visual Foxpro的卓越性能是无法代替的。新版 Visual Foxpro 9.0在Foxpro社区引起了轰动,新版中将包含一个更快的本地数据引擎,支持更多的数据类型,SQL语句执行中有更大的一致性,一个完全重新设计的可扩展报表编写器,以及一系列效率和功能增强特性。

  在Visual FoxPro 9.0的报表系统出现了难以置信的改进。在本文中,我只讨论几个新增功能中的一个——扩展运行时报表引擎(runtime reporting engine)的能力。

  Visual FoxPro小组在处理运行时改进工作时一直紧记着几个目标,包括: 处理打印和预览之外的多种报表输出 使用GDI+进行报表输出。这会带来很多显著的改进,例如精确地显示、图像和字体的平滑调整,以及一些附加的能力(例如文本旋转) 提供一个更加灵活和可扩展的报表系统   你可以同时访问旧的和新的报表引擎,因此你可以根据需要选择在哪种引擎下运行报表。但是一旦你看到新的报表引擎的优点,你就再也不希望使用旧式报表了。

  报表系统的架构

  Visual FoxPro 9以前版本中的报表系统类似于单片电路:它处理所有细节信息,只有少量的例外(用户定义的函数、报表带条的OnEntry和OnExit表达式等等),在报表运行的时候,你是不能与它交互操作的。

  新的报表引擎把报表的功能分成了两部分:现在的报表引擎只处理数据和对象定位;一种新对象(报表监听器)处理显示和输出的事务。由于报表监听器是类(class),因此我们现在可以使用以前梦寐以求的方式与报表进程交互操作。

  新的报表语法

  Visual FoxPro 9支持使用旧的报表引擎运行报表;你可以像以前一样使用REPORT命令(尽管你可以使用新命令重载REPORT的行为)。为了得到新式的报表行为,必须使用REPORT命令的OBJECT子句。OBJECT子句支持两种使用方法:指定报表监听器和指定报表样式。微软把它归纳为对象辅助(object-assisted)报表。

  报表监听器是提供新式报表行为的对象。报表监听器是基于Visual FoxPro 9的新的基础类ReportListener的。为了让Visual FoxPro 9使用报表指定的监听器,需要实例化监听器类,并在REPORT命令的OBJECT子句中指出该对象的名称。下面是一个例子:

loListener = createobject('MyReportListener')
report form MyReport object loListener
  如果不希望手动实例化监听器,可以通过指定报表类型让Visual FoxPro自动为你完成实例化过程,例如:

report form MyReport object type 1
  已经定义好的类型有: 0——从打印机输出 1——预览 2——某个时刻的页面信息模式,但不会输出到打印机 3——所有页面模式,但不会调用预览窗口 4——XML输出 5——HTML输出   当然我们还可以使用其它的用户自定义类型。

  使用这种方式运行报表的时候,将调用新的_REPORTOUTPUT系统变量(默认情况下它位于Visual FoxPro主目录的ReportOutput.APP中)中指定的应用程序来决定指定该类型使用哪种监听器类来实例化。它是通过查看APP中内建的监听器注册表(尽管你可以指定它使用一个外部表)中的监听器类型来实现的。如果它找到了需要的类,它就实例化该类并传递该监听器对象的一个引用到报表引擎中。因此,在REPORT命令中使用OBJECT TYPE 某种类型的效果与下面的代码的效果是相同的:

loListener = .NULL.
do (_ReportOutput) with SomeType, loListener
report form MyReport object loListener
  报表监听器

  在报表运行的过程中,伴随着报表事件的发生,Visual FoxPro把这些事件暴露给基于ReportListener基类的对象。Visual FoxPro帮助文件记录了ReportListener的属性、事件和方法(PEMs),但是在本文中我只讨论其中最有用的一些。

  表1列举了ReportListener类的最常使用的一些属性。

  表1:ReportListener类的一些有用属性

属性
描述
CurrentDataSession
报表数据的数据对话ID
FRXDataSession
FRX游标的数据对话ID
GDIPlusGraphics
用于显示的GDI+绘图对象句柄
ListenerType
监听器生成的报表输出类型。默认值是-1(无输出),你应该把它改成符合需要的值。它的值与REPORT 命令的OBJECT TYPE 子句中规定的值是相同的。
OutputPageCount
被显示的页面的数量
QuietMode
如果它的值为.T. (默认值是 .F.) 就支持进度信息

  表2显示了经常使用的ReportListener的事件和方法。

  表2:ReportListener的一些有用的事件和方法

事件/方法
描述 LoadReport
在FRX被载入和打印机假脱机操作被打开前调用 UnloadReport
在报表运行之后调用 BeforeReport
在FRX被载入但是报表运行前调用 AfterReport
报表运行后调用 BeforeBand
处理某个报表条带前调用 AfterBand
处理某个报表条带后调用 EvaluateContents
显示某个字段前调用 Render
显示对象的时候调用 OutputPage
向特定的设备输出指定显示的页面   ReportListener子类

  Visual FoxPro主目录中的FFC(FoxPro基础类)子目录包含了_ReportListener.VCX文件,该文件包含了ReportListener的一些子类,而这些子类的功能比基类更多。这些子类中最有用的是_ReportListener。

   _ReportListener最重要的特性之一是对继承(successors)的支持。当你运行报表的时候,你可以希望使用多个报表监听器。例如,如果你希望预览某个报表,同时输出为HTML,就会涉及到一个以上的报表监听器。_ReportListener通过提供Successor(它包含一个引用另一个监听器的对象)属性允许我们构建监听器链。

  例如,假设ListenerA和ListenerB都是_ReportListener的子类,它们各自执行某些事务,并且你希望在某个报表上同时使用这两个监听器。下面是把这些监听器链接起来的代码:

loListener = createobject('ListenerA')
loListener.Successor = createobject('ListenerB')
report form MyReport object loListener
  报表引擎只与REPORT或LABEL命令中指定的监听器(称为lead listener,头监听器)通讯。当报表引擎引发报表事件的时候,头监听器调用它的后继者的适当方法,而后继者又调用自己的后继者的适当方法,这样一直沿着链进行下去。这种架构就是响应链,链中的任何监听器都可以决定执行某些操作或者把消息传递给链中的后继节点。

   _ReportListener的另外一种有趣的能力是链接报表。AddReport方法把一个报表添加到定制的ReportFileNames集合中。你给这个方法传递报表名、可选参数还有将要使用的报表子句(例如RANGE子句)和另一个监听器对象的引用。RemoveReports方法从集合中删除所有的报表。RunReports运行报表;传递进去的第一个参数是.T.的时候将在报表运行后从集合中删除报表,第二个参数为.T.时将忽略AddReport指定的任何监听器。下面是一个示例,它运行了两个报表,但是表面看起来好像是一个报表:

loListener = newobject('_ReportListener', home() + 'ffc\_ReportListener.vcx')
loListener.ListenerType = 1
loListener.AddReport('MyReport1.frx', 'nopageeject')
loListener.AddReport('MyReport2.frx')
loListener.RunReports()
  HTML和XML输出

  由于开发小组的设计目标之一是提供更多的报表输出类型,所以Visual FoxPro 9包含了_ReportListener的两个子类,叫做HTMLListener和XMLListener,分别用来来提供HTML和XML输出。这些监听器都内建在ReportOutput.APP中,但是在_ReportListener.VCX中也可以使用。
监听器类型5指定为HTML输出、4指定为XML输出,因此你可以使用下面的命令把输出指定为HTML:

report form MyReport object type 5
  但是如果这样操作你将无法控制将要建立的文件名和其它一些设置。作为替代,调用ReportOutput.APP可以让你得到需要的监听器引用、设置需要的属性、接着告诉REPORT命令使用该监听器。

  下面的代码从MyReport报表中建立了一个叫做MyReport.HTML的HTML文件。当你指定为类型5时,ReportOutput.APP使用自己内建的HTMLListener类提供输出。

loListener = .NULL.
do (_reportoutput) with 5, loListener
loListener.TargetFileName = 'MyReport.html'
loListener.QuietMode = .T.
report form MyReport object loListener
  下面的代码从MyReport报表中建立MyReport.XML文件,只包含了数据。在这种情况下,代码片断使用了XMLListener类(类型4)。

loListener = .NULL.
do (_reportoutput) with 4, loListener
loListener.TargetFileName = 'MyReport.xml'
loListener.QuietMode = .T.
loListener.XMLMode = 0
&& 0 = data only, 1 = layout only, 2 = both
report form MyReport object loListener
   HTML输出实际上使用XML监听器生成XML,接着使用XSLT来生成最终的HTML。

  这两个监听器类都有一些附加的属性,你可以使用这些属性进一步控制输出。我推荐你查阅一下Visual FoxPro文档。此外,由于它们是_ReportListener的子类,所以监听器类支持_ReportListener类的能力,包括链接监听器和运行多个报表。下面是一个同时输出XML和HTML的示例:

use _samples + 'Northwind\Orders'
loListener1 = .NULL.
do (_reportoutput) with 4, loListener1
loListener1.TargetFileName = 'MyReport.xml'
loListener1.QuietMode = .T.
loListener1.XMLMode = 0
&& 0 = data only, 1 = layout only, 2 = both
loListener2 = .NULL.
do (_reportoutput) with 5, loListener2
loListener2.TargetFileName = 'MyReport.html'
loListener2.QuietMode = .T.
loListener1.Successor = loListener2
report form MyReport object loListener1
  建立自己的监听器

  由于报表监听器是类,所以报表运行的时候,你可以建立子类来改变报表系统的行为。

  例如,我一直希望在运行时动态地格式化字段。在某些条件下,我希望字段用红颜色打印,其它条件下用黑颜色打印。一个字段有时需要加粗而其它时候则不需要。

  改变字段在报表中的显示样式的关键是EvaluateContents方法。这个方法在字段被显示之前调用每个字段对象,赋予监听器改变字段样式的权力。该方法的第一个参数是被处理的字段对象的FRX记录号,第二个参数是包含属性和字段对象信息的对象(请查看Visual FoxPro帮助文件中该对象包含的属性列表)。你可以修改任何属性来改变报表中字段的样式。如果你是这样做的,那么还需要把该对象的Reload属性设置为.T.,以通知报表引擎你已经改变了一个或多个属性。

  列表1显示了定义_ReportListener的一个子类(叫做EffectsListener)的代码片断,该子类处理可能应用于报表中的字段的不同效果类型。这些效果通过效果处理对象来应用,而这些对象都存储在EffectsListener的oEffectsHandlers属性的集合中。每种效果处理对象处理一种效果。

  在报表被处理的时候,监听器需要确定哪些字段应用了效果。它在EvaluateContents方法中查看每个将要显示的字段,实现这种功能。EvaluateContents调用SetupEffectsForObject,它调用每个效果处理程序的GetEffect方法来决定是否给该字段应用某种效果。GetEffect查看FRX中的字段记录的USER备注来指令应用哪种效果。如果该字段需要某种特定的处理程序,该处理程序就被添加到处理该字段的处理程序集合中(因为每个字段可能应用多个效果)。

  这意味着在每条记录的每个字段上都会调用EvaluateContents,可是没有必要在一个特定字段上进行多次效果检查(这样做将导致报表性能下降)。因此,BeforeReport建立了一个数组,它存储了FRX中记录的行。如果该数组的第一列为默认值.F.,说明监听器还没有检测将要显示的字段的效果,因此EvaluateContents做出检测并把该数组的第一列设置为.T.,这样FRX就不会再次检测了。

  在检测某个字段是否应用了效果后,EvaluateContents进入到该字段的效果处理程序集合中,调用每个程序的Execute方法执行必要的操作。

   DynamicForeColorEffect就是一个效果处理程序。它用下面的格式查看报表中某个字段的USER备注:

*:EFFECTS FORECOLOR = expression
(你可以从某个对象的属性对话框中的“其它”选项页中看到该对象的USER备注。)
  列表1中使用的TestDynamicFormatting报表的ORDERDATE字段的USER备注中有下面的代码片断指令;它告诉EffectsListener:DynamicForeColorEffect对象应该调整字段的颜色,当装运时间大于订单时间10天以上就用红颜色显示,否则就用黑颜色显示:

*:EFFECTS FORECOLOR = iif(SHIPPEDDATE > ORDERDATE +10, rgb(255, 0, 0), rgb(0, 0, 0))

图1:TestDynamicFormatting报表。列表1中的代码生成这个报表,它演示了对装运日期和装运形式列的动态格式化。
   DynamicForeColorEffect的Execute方法通过把传递到EvaluateContents中的字段属性对象的PenRed、PenGreen和PenBlue属性设置为适当的颜色,并把Reload设置为.T.(告诉报表引擎已经做了一些修改)来改变字段的颜色。

  DynamicStyleEffect使用类似的指令来改变字体样式。此处使用的样式必须是一个数值:0是正常体、1是粗体、2是斜体、3是粗斜体。TestDynamicFormatting报表中的SHIPVIA字段的USER中有下面的指令,它引起SHIPVIA为3(因为该字段的表达式实际上显示为Mail)的字段显示为粗体,否则为正常体。

*:EFFECTS STYLE = iif(SHIPVIA = 3, 1, 0)
   DynamicStyleEffect的工作方式与DynamicForeColorEffect类似,只是改变了字段属性对象的Style属性。

  运行TestDynamicFormatting.PRG将出现图1所示的输出结果。   自定义显示

  你不仅可以改变字段的外形——你还几乎可以在报表监听器中执行自己需要的任何事务。ReportListener的Render方法负责在报表页面上绘制每个对象。你可以重载这个方法来实现各式各样的输出。

  实现自定义显示的监听器当然需要使用GDI+函数。GDI+是执行图像操作 和输出的数百个Windows API函数的集合。

  为了更方便使用GDI+函数,Visual FoxPro的FFC目录中包含了_GDIPlus.VCX。_GDIPlus由新西兰Cornerstone软件公司的Walter Nicholls编写,它由GDI+函数的包装类组成,使这些函数更易于使用,同时还是面向对象的。Visual FoxPro帮助文件中的“GDI+ API包装基础类”主题列举了这些类,并提供了它们的少量背景信息。这个类库对于执行GDI+显示有很大的帮助,因为你在使用它们的时候,不需要知道GDI+的太多相关信息。我也不太了解GDI+的很多信息,但是仍然在几个小时之内建立了接下来要讨论的监听器类。


图2:设计时的TestColumnChart.FRX样式
  列表2中的代码来自TestColumnChart.PRG,它运行了图2中所示的TestColumnChart.FRX报表,建立了图3所示的输出。请注意,输出结果与报表布局之间有很大的差别,字段和形状(shape)没有显示出来,而绘制示例Northwind数据库中的Category_Sales_For_1997视图的内容的条状图却显示出来了。这部分原因是字段上的Print When子句防止它们被打印出来,但最大的原因在于这个报表使用的监听器类(ColumnChartListener)把Summary(汇总)报表条带中的形状对象更替为列条状图。

  下面让我们看看这个监听器是如何实现这种功能的。

  ColumnChartListener的Init方法把aColumnColors数组初始化为报表中的列将会使用到的颜色。请注意,GDI+的颜色与RGB()函数返回的值有一点点不同,因此它使用CreateColor方法来进行必要的转换。如果你希望使用不同的颜色集,你可以从ColumnChartListener衍生出子类或者实例化ColumnChartListener之后,在数组中存储另一组颜色集合。请注意,我们只定义了八种颜色,如果报表中的列多于八个,每种颜色可能用于多个带条。


   图3:列表2中的代码生成这个报表,它建立了带状图而不是传统的输出。
  BeforeReport方法实例化一个GPGraphics对象到自定义的oGDIGraphics属性中。GPGraphics是_GDIPlus.VCX中的一个类。它和其它_GDIPlus类都被用在DrawColumnChart方法中来绘制条状图的组件。

   GPGraphics需要一个将要显示的GDI+表面的句柄。幸运的是监听器已经有这样一个句柄,存储在GDIPlusGraphics属性中。唯一的复杂因素是该句柄在每个页面上都会改变,因此当标题或页面头部带条被处理的时候,BeforeBand方法(在报表条带被处理前调用)调用GPGraphics对象的SetHandle方法来赋予它句柄。

  在报表被处理的时候,监听器必须确定图表中的标签和值来自于何处。在字段将要被显示的时候,它在EvaluateContents方法中通过查看每个字段得到这些信息。如果该字段在FRX中的USER备注包含了LABEL(与CategoryName字段中一样),就表明该字段应该用于条状图的标签。USER备注中的DATA(例子中是CategorySales字段)表明这个字段用作图表的值。有了前面讨论过的EffectListener类之后,根本就不需要多次检查USER备注,因此在示例中使用了相同的机制——在一个数组属性中存储标识以表明某个字段是否被处理过。

  如果监听器仍然没有检测某个将显示的字段的USER备注,EvaluateContents将执行这种检测,设置数组中的标识以表明该字段是否用作标签或值,并把数组的第一列设置为.T.,这样FRX记录就不会再次检查了。如果某个字段用作标签或值,EvaluateContents相应地更新aValues数组。

   AdjustObjectSize与EvaluateContents类似,除了它在形状(shape)上而不是在字段上调用。AdjustObjectSize检查当前形状的FRX记录的USER备注中是否存在COLUMNCHART,如果存在就表明这个形状应该被条状图代替。有了EvaluateContents之后,监听器只需要检查一次,因此它也使用了相似的逻辑。

   Render方法负责在报表上绘制对象。如果将被绘制的对象是一个被条状图代替的形状,它就调用自定义的DrawColumnChart方法,带上NODEFAULT防止该形状被绘制出来。否则,该对象会被正常地绘制出来(请注意,如果没有DEDEFAULT(),初始的行为是绘制对象,因此这个参数是必要的)。

   DrawColumnChart计算出图表显示的最大值,这样它才知道条带应该多大,接着它从_GDIPlus类中建立一些执行绘制操作的对象。它调用DrawLine方法绘制出图表的垂直和水平边界,接着进入aValues数组,使用DrawRectangle绘制出每个值的条带并使用FillRectangle用适当的颜色填充。DrawColumnChart使用相同的DrawRectangle和FillRectangle方法绘制方框、使用DrawStringA绘制标签,给图表添加了一个方框和标签图例。

  其中一些绘图属性来自于自定义属性中的值,这使得绘制图表更加灵活。例如,属性cLegendFontName和nLegendFontSize指定了图例标签使用的字体和大小,nLegendBoxSize指定了将要绘制的方框的大小。你可以在代码列表2开始处看到这些属性的注释。

  微软已经揭开了Visual FoxPro报表系统的面纱!通过给ReportListener对象传递报表事件,我们可以与这些事件交互作用来执行一些自己希望实现的事务,其范围从提供各式各样的输出类型到动态地改变被显示的对象。Visual FoxPro团体使用这些新特性会实现什么样的效果?难以想象!


阅读全文(2236) | 回复(0) | 编辑 | 精华 | 删除
 


Visual FoxPro 6.0网络编程应注意的问题
软件技术

fermos的博客 发表于 2005/9/8 8:06:07

Visual FoxPro 6.0网络编程应注意的问题 http://www.pcdog.com 2004-12-19 计算机与信息技术   本文介绍了Visual FoxPro 6.0网络编程中应注意的几个问题。


    1、引言

    笔者在使用Visual FoxPro 6.0编制超市综合信息管理系统时,由于某些参数的设置不当,或某些命令使用不当,曾走过一些 弯路,总结起来体会颇多。

    2、Visual FoxPro 6.0的几个不尽如意的地方

    2.1 Visual FoxPro 6.0的一个bug

    笔者在设计一个产品库存查询表单(form)kccx.scx时,其中一个网格(grid)grid1显示查询内容,运行此表单,因尚未选择查询条件,网格显示所有产品的库存情况。因此,在表单kccx.scx的active事件中加入以下程序,使网格grid1显示表dspbmk(产品库存库)的全部内容:

select  *  from  dspbmk  into  table  tj  
thisform.grid1.recordsource="tj"
thisform.grid1.scrollbars=3
thisform.refresh

    系统刚开始运行时,由于产品记录个数少并未发现问题,但随着产品的增加,发现进入此表单很慢。通过将SET SAFETY 设置为ON,单步调试,发现上述程序的第一条SELECT语句重复执行若干次。将此段程序移到表单的init事件后,程序执行正常。或者将上段程序该为:

select  *  from  dspbmk  into  cursor  tj  
thisform.grid1.recordsource="tj"
thisform.grid1.scrollbars=3
thisform.refresh

    程序也执行正常。

    同样,将上段程序放入页框(pageframe)的某一页(page)的active事件中,也会出现同样的问题。笔者并未找到关于此问题解释的相关资料,也许这是Visual FoxPro 6.0的一个bug。

    2.2 慎用VFP的缓冲区功能

    Visual FoxPro 6.0提供了缓冲区功能,只要将缓冲区(buffering)状态设为2、3、4、5,那么对表或记录的操作只在缓冲区中进行,再通过tableupdate()函数将修改传送到表中,或通过tablerevert()函数取消对表的修改。VFP的这种功能大大简化了程序的编写。但提醒编程者注意:当使用表缓冲(buffering状态设为5)功能时,若一次修改的记录数太多(比如400条以上),当执行tableupdate()函数对表进行真正修改时,发现缓冲区的数据丢失,本次修改失败。

    2.3 多使用FLUSH命令

    假定有一表单,要连续执行若干小时,其中经常要对某个表操作,在表单的load事件中打开此表,此后该表一直处于打开状态,通过APPE、DELE、REPL等操作对表进行了上千条记录的操作,但当退出后有时会发现当天的数据并未保存,尽管VFP帮助文件中介绍5分钟后会自动FLUSH。在每次操作后加入FLUSH语句,再没出现以上问题。

    3、Visual FoxPro 6.0网络程序设计应注意的几个问题

    在单用户状态下,数据库的使用没有共享和独占概念,用户可以随意对表进行增、删、改、查等操作,不受任何影响。但是,在网络环境下就不一样,当表被打开时,如果两个用户对一个表同时进行修改、删除等操作,后果将不堪设想。所以在网络环境下表的打开有两种方式:一是共享方式,表示这个表可以被任何用户操作;二是独占方式,表示这个表只能被一个用户操作。例如,对于商场管理系统,由于数据量大,需经常对表进行数据整理(需执行PACK、ZAP等操作),当系统管理员进行数据整理时表必须以独占方式打开;而前台收款和后台操作同时进行时,表必须以共享方式打开。那么如何解决多个用户同时对表操作而不致出现问题呢?下面笔者谈几点体会。

    3. 1  一般的解决方法

    文件或记录的加锁与解锁是网络程序设计中经常用到的方法。VFP提供了FLOCK()、RLOCK()和LOCK()函数对文件和记录进行加锁,一个文件或记录加锁后,只能由加锁用户进行读写,其他用户只能读不能写。当对记录或文件进行修改或删除时,必须加锁。当对文件操作完毕应及时解锁,VFP提供UNLOCK命令对指定工作区解锁。如:

do  while  !flock()
wait window ‘正在锁定数据库请稍候!’Timeout 0.05
enddo  

unlock

    当对文件或记录加锁时,若由于某种原因没有锁住,可以再试加锁操作,并可指定加锁次数或试锁的时间。VFP提供了SET  REPROCESS  TO命令来确定如何控制失败的记录或文件加锁。此外,VFP还提供SET  REFRESH  TO命令控制多长时间后显示网络中其他用户对记录所做的修改。

    3.2  慎用VFP的隐含锁定功能

    当VFP处于隐含锁定状态(即SET  LOCK 设置为ON)时,执行某些命令时将自动对表加锁。这些命令包括:

    AVERAGE、CALCULATE、LIST、DISPLAY、SORT、INDEX、COUNT、TOTAL、SUM、COPY  TO、COPY TO ARRAY 、REPORT、JOIN、LABEL。

    在网络环境中,必须将SET  LOCK 设置为OFF,以免影响其他用户的正常使用。例如,一个商场管理系统,后台正在打印商品信息,若SET  LOCK 设置为ON,则此时商品信息表将加锁,前台销售需对商品信息表的商品库存进行减操作,也需对商品信息表加锁。此时将陷入锁定等待状态,影响前台收款。

    3.3  使用VFP的多记录锁功能时要及时对记录开锁

Visual FoxPro 6.0将SET  MULTILOCKS设置为ON,即设置多记录锁,表示同时对多条记录加锁。对此功能的使用需谨慎,一定要及时开锁,否则容易引起死锁。假定有一个网络版的商场销售系统,许多表单的对表操作采用了表缓冲区状态,由于VFP规定使用缓冲区功能必须将SET  MULTILOCKS设置为ON。所以系统运行时是处于多锁状态。前台销货存盘时要对商品信息表(dspbmk.dbf)中的商品库存量进行修改,在存盘按钮的click事件中加入以下程序段:
sele  sp        /*  销售商品清单
go  top
do while  .not. eof()
  sele  dspbmk
  seek  sp.spbh
  do  while  !rlock()
    wait window ‘正在锁定数据库请稍候!’Timeout 0.05
  enddo  
  repl  kcl  with  kcl-sp.sl
  sele  sp
  skip
enddo
sele  dspbmk
unlock  in  dspbmk

    程序对每个记录修改后并未及时开锁,在全部修改完后才开锁。

    假定有甲乙两个收款台,前台甲正在卖编号为1、2、3的商品,前台乙正在卖编号为3、2、1的商品,甲存盘时首先锁定1商品,库存量修改完后记录指针移动,又锁定了2商品的记录,处理完后试图锁定2商品;而前台先锁定了3商品记录,处理完后试图锁定2商品。这时就发生死锁,造成系统瘫痪。纠其原因,主要因为SET  MULTILOCKS设置为ON了,而以上程序却未对记录及时开锁。



阅读全文(2761) | 回复(1) | 编辑 | 精华 | 删除
 


VFP6.0中实现记录删除后自动调整编号
软件技术

fermos的博客 发表于 2005/9/8 8:05:06

VFP6.0中实现记录删除后自动调整编号 http://www.pcdog.com 2004-12-1 Yesky   好,言归正传,下面就来看看笔者是怎么样实现记录删除后自动调整记录编号这一功能的。

  首先大家来看一下我的这个人事管理系统的删除表单:见图1,这里面的各个控件我想大家一看就知道是干什么用的了,在此我就不再介绍,有一点要说明的是,数据库(或表)打开的时候是不能带除了编号以外的索引的,也就是说必须要以编号 排序的方式。

     

  在图1的表单中,“取消”,“确定”这两个按钮的代码笔者就不详细介绍了,根据程序作用的不同这两个按钮的代码可能会有一些出入。“上一个”和“下一个”按钮的代码也很简单,主要是一些判断记录有没有到头或到尾的代码和跳转记录时刷新的代码,在此也不多做介绍。下面我们主要来看看“删除”这个按钮的代码,整个“删除”按钮的代码如图2所示:下面我来介绍一下各段代码的作用:

    

===========================================
scyesno=messagebox("真的要删除吗?",4+32,"确认") &&这一句不用我多说吧?产生一个“确认”对话框。
if scyesno=6 &&如果用户按下“确定”按钮。
 b=recno() &&首先把要删除的记录号储存下来,以下要用到。
 delete &&把选定的记录作删除标记。
 pack &&把选定的记录从数据库中彻底删除。
 messagebox("已经搞定!",0+64,"删除完毕") &&产生一个“删除完毕”的对话框。
 go bottom &&数据库的记录指针移到尾部。
if reccount()>0 &&判断表中的记录个数是不是大于0,即还有没有记录。如果刚才删除的是数据库中最后  一条记录,不加这条判断语句截止的话 再向下执行会出错!所以这是一条相当重要的判断代码。
 if b<recno() .or. b=recno() &&如果刚才删除的记录号小于或等于最后一条记录的记录号。(在此注意   一下上面“go bottom”这句代码的作用。)
  go b &&跳转到刚才删除的记录的记录号上,从这一条记录开始修改记录的编号。
  do whil .not. eof() &&如里不到数据库结尾则重复执行以下修改编号的代码:
   replace 编号 with 编号-1 &&把当前记录的编号减1并保存。
   skip &&跳转到下一条记录。
  enddo &&结束假设和循环。
  go b &&这句要不要都行,主要是为了方便在表单里显示删除的那条记录的下一条记录的内容的。
 endif &&结束判断。
endif &&结束判断。
endif &&结束判断。
  &&以下的代码是为了删除后刷新用的,大家一看就明白的,不用我说了。

thisform.text1.refresh
thisform.text2.refresh
thisform.combo1.refresh
thisform.text4.refresh
thisform.text5.refresh
thisform.text6.refresh
thisform.text7.refresh
thisform.edit1.refresh
ThisForm.Oleboundcontrol1.refresh
===============================================

  好了,代码向大家看完了,在此笔者再向大家介绍个建议:就是在图1的表单中,如果这是个修改记录的表单的话,有相当一部分书或资料上都介绍把各个控件的controlsource的值直接指向数据库中相对应的字段名,笔者认为这样做不是很好,因为这样的话如果不小心动了表单中各个文本框的值的话,程序是没有一点提示就直接修改数据库中相对应的字段的值的。笔者的做法是:把各个控件的controlsource的值指向一个变量,比如说“姓名”文本框的controlsource值指向xgxm,然后在“确定修改”按钮的代码里再用“replace 姓名 with alltrim(xgxm)”来真正修改数据库中的记录,而用户不按下“确定修改”这个按钮是不会修改记录的内容的,这样就避免了误操作而修改记录的可能性。当然在“确定修改”按钮的开头你还可以加上个“警告框”来作为提醒。

阅读全文(2069) | 回复(0) | 编辑 | 精华 | 删除
 


VFP中焦点行的动态显示
软件技术

fermos的博客 发表于 2005/9/8 8:03:41

VFP中焦点行的动态显示 http://www.pcdog.com 2004-10-28 互联网 李明亮

在用VFP设计一个应用系统时,信息的浏览是其中一个相当重要的功能。人们一般用Grid对象来显示数据信息,但该对象在使用功能上却存在一些不足。
提出问题
当使用者在记录之间移动焦点时,只有获得焦点的记录的某一个字段以不同于Grid背景的颜色显示。为使整条记录均用同一种颜色突出显示(本文示例为蓝色背景),即当数据记录移动时,用颜色动态 变化显示相对光标所在记录的位置,可以在Grid对象的afterRowColChange事件过程中加入如下内容:
this.setall(“dynamicbackcolor”,“iif(this.activerow=recno(),rgb(0,0,255),rgb(255,255,255))”,“column”)。
笔者在使用中发现:如果设置了Set delete on环境参数,则当逻辑删除记录后,动态背景的颜色显示就会出现混乱。
分析问题
这种情况一般有两种解决方法:
●执行pack命令,再重新创建和设置Grid的各项属性;
●先将没有删除标志的记录复制到一个临时表中,用zap命令删除源表中所有记录,再将没有删除标志的记录从临时表追加到源表中。
这两种方法实质上都是把已逻辑删除的记录从数据表中真正删除,缺点是两种方式都要求该表必须以独占方式打开。
如果表是以共享方式使用,那么又该如何处理呢?其实,动态背景颜色不能正确显示的原因是由于逻辑删除记录,该操作造成光标所在的行activerow()不等于数据的记录号recno(),从而导致了动态背景颜色的显示出现混乱。笔者通过两个表的关联成功地解决了这一难题。
解决问题
我们以一个实际的例子来说明如何解决这一问题。设有一个表cenji.dbf,表的字段分别为:学号、姓名、姓别、成绩。新建一个表单,并创建一个Grid对象,设置Grid对象的recordsource属性为cenji,在Grid对象的afterRowColChange事件过程中添加如下代码:
LPARAMETERS nColIndex
if this.activerow = recno()
this.setall(“dynamicbackcolor”, “iif
(this.activerow=recno(), rgb(0,0,255),rgb(255,255,255))”,“column”)
else
select 学号from cenji where .not. deleted() into cursor temp_table
select temp_table
index on 学号 tag xh
set relation to 学号 into temp_table in cenji
sele cenji
this.setall(“dynamicbackcolor”,“iif(this.
activerow=recno(‘temp_table'), rgb(0,0,255),rgb(255,255,255))”,“column”)
endif
thisform.refresh
在上述代码中,当逻辑删除记录造成光标所在的行activerow()不等于数据的记录号recno()时,只需对数据建立一个关联子表,当记录指针在父表cenji中移动时,子表temp_table的记录指针移到学号相同的记录上。子表的记录号是不包括逻辑删除记录的,保证了光标所在行的子表记录号recno(‘temp_table')等于光标所在的行activerow(),从而使该行动态背景颜色能正确显示。本文为了说明方便, 在afterRowColChange事件过程中建立子表,实际上最好在Delete等命令之后建立。
另外,当activerow属性与recno()函数配合使用动态显示背景颜色时,一般会提示用户“请不要在所显示的记录中设定主索引”,否则不能正确动态显示。实际上,按照如上方法,同样可以解决这一问题。
设表cenji.dbf中,按成绩字段建立一个主索引,在Grid对象的afterRowColChange事件过程中写下如下代码:
LPARAMETERS nColIndex
select 学号 from cenji into cursor temp_table order by 成绩 desc
select temp_table
index on 学号 tag xh
set relation to 学号 into temp_table in cenji
this.setall(“dynamicbackcolor”,“iif
(this.activerow=recno(‘temp_table’), rgb(0,0,255),rgb(255,255,255))”,“column”)
thisform.refresh
同样,不一定要在Grid对象的afterRowColChange事件过程中建立关联子表temp_table。
本文代码在中文版Windows 98和Visual Foxpro 6.0中运行通过。

阅读全文(1858) | 回复(0) | 编辑 | 精华 | 删除
 


在VFP报表中实现每页打印指定记录数
软件技术

fermos的博客 发表于 2005/9/8 8:00:55

在VFP报表中实现每页打印指定记录数 VFP在各企事业单位中有着广泛的应用,我用它开发了本单位使用的元器件检测信息管理系统,现在是本单位日常工作中不可缺少的管理工具。开发中自有许多心得,但最想跟大家谈的是在报表中实现每页打印指定记录数这个问题。

  方法一

  在VFP的报表生成器中,编制报表,用调整报表页头带和页脚带高度的方法来控制每页要打印的记录数据,这是一种比较简单、容易实现的方法。但是由于不同的计算机中默认的纸张大小不一样,或者相同的纸型中的页边距设置不一样而使得每页打印的记录数发生改变。这对于固定的用户和固定的打印机、纸张来讲,没有什么问题,但如果软件要运行于多台电脑上或推广到更广泛的用户中,就要考虑不同的打印机及纸张的问题了。此时可以用下面介绍的方法二和方法三来实现。

  另外,有很多用户要求当表中的记录数不是整页数时在最后一页中以空的表格填满报表,也只有在下面的两种方法中才能实现。

  方法二

  以下过程中假设报表中使用的表名为 TEMP.DBF

  在报表设计器中:

  1、将报表中的打印字段前面的别名前缀全部删除。

  2、将报表数据环境中原来的表从数据环境中移去。

  3、在报表的数据环境的 Init 中放入以下代码:

  *-- DetailNum 是细节带中要打印的记录数,可在这里按需要进入修改

  local DetailNum

  DetailNum = 17

  *-- 以下 SQL语句消除删除记录对分组字段的影响及对数据进行适当的排序

  SELECT * FROM TempsintosTABLE TMP1swheresNOT DELETED() &&&&sgroupsby 排序条件

  SELECT int((recno()-1)/DetailNum) as GroupCount, * ;

   FROM Tmp1 ;

  sintosCURSOR Temp2

  USE IN TMP1

  *-- 为保持一个干净的环境,删除刚刚建立的临时过渡表

  delete file TMP1.dbf

  if file("TMP1.fpt")

   delete file TMP1.fpt

  endif

  *-- 注:你可以在上面的代码中设置 DetailNum 的值为你所希望每页报表要打印的记录数。

  *-- 你也可以在第一句 SQL Select 中按你的需要设置sgroupsby 子句来排序数据。

  4、在报表中增加一个分组并设置分组表达式为 GroupCount(即上面第二个 SQL Select 语句中的第一个字段名), 并选择每组从新的一页上开始复选框。

  5、运行你的报表。

  6、最后要说明的是:在运行报表前应设置正确的路径以便 VFP 可以找到报表所使用的表。

  方法三

  在该方法中,定义了一个全局变量tobePrint,该变量的值就是每页要打印的记录数,如果没有定义该变量,则使用默认的每页打印 15 条记录。

  1、temp.dbf添加到报表数据环境中。

  2、将temp.dbf的数据环境中的 BufferModeOverride 属性设置为 5。

  3、在数据环境属性Destroy 事件中写:

  =tablerevert(.t.)

  4、在数据环境属性Init 事件中写:

  if vartype(tobePrint) = "U" &&&& 如果全局变量还没有定义,在这里定义它的默认值为15

  Public tobePrint

  tobePrint = 15

  endif

  *-- 以下代码是为了当要打印的记录数不满一页时,打印空行来填满整个报表页

  do while reccount("temp") % tobePrint <> 0

  append blank

  enddo

  5、 在报表中新建两个报表变量如:nCount、nGroup,变量nCount的计算选项组中选择计数,在变量 nGroup 的要存储的值中写:

  iif(nCount<>0 and nCount % tobePrint=0,nGroup+1,nGroup)

  6、 在报表中增加一个分组并设置分组表达式为nGroup,并选择每组从新的一页上开始复选框。

  7、 用以下代码来运行报表:

  use temp

  *-- 为避免重复定义变量错误,在定义全局变量前首先释放它

  release tobeprint

  *-- 定义全局变量,该变量中保存了每页要打印的记录数

  public tobeprint

  *-- 设置每页打印10条记录

  tobeprint = 10

  *-- 打印我们的报表

  report form temp preview

  方法二与方法三的异同

  二者都是采用了数据分组的方法来实现“每页打印指定记录数”,向表中添加空记录来实现“表中的记录数不是整页数时在最后一页中以空的表格填满报表”。

  二者不同之处在于:方法二是利用临时表来解决问题,数据的分组信息写在临时表中,方法三是利用报表变量来解决分组问题,数据分组是用报表变量来实现的。 

阅读全文(2739) | 回复(0) | 编辑 | 精华 | 删除
 


在FOXPRO FOR WINDOWS中播放声音文件的三种方法
软件技术

fermos的博客 发表于 2005/9/8 7:59:33

在FOXPRO FOR WINDOWS中播放声音文件的三种方法  —、使用通用字段播放声音文件   foxpro 2.5 for windows,数据库中的通用字段可以链接或嵌入声音对象(.wav格式的声音文件)。其命令格式如下:   append general<general field>from<file>   其中<general field>为通用字段名称,<file>为含路径和扩展名的声音文件。 命令:@<row,column>say<general field>可以实现声音的播放。   二、使用set bell to 命令直接播放声音文件   set bell to 命令不仅可以设置计算机中开关响铃及响铃信号属性,还可用以播放声音文件:   WAV_NAME=“SOUND.WAV” &&SOUND.WAV是要播放的声音文件名
  SET BELL TO WAV_NAME,0 &&设置响铃信号为波形声音文件??CHR(7) 三、调用WINDOWS的“录音机”播放声音文件:   我们还可以通过调用WINDOWS3。X中的“录音机”或声卡提供的类似工具播放声音文件,下面是调用“录音机”的方法:   WAV_NAME="SOUND.WAV"
  RUN /N SOUNDREC.EXE & WA_NAME
  &&SOUNDREC.EXE是“录音机”程序的文件名   需要说明的是:用前两种方法播放声音的操作均不是交互式的,都不能控制播放的过程,在实际使用中很不方便。此外第一种方法需要把声音对象加入到数据库之中,不仅会使数据库的备注文件变得非常庞大,而且将多占用近一倍硬盘空间(如果不删除。WAV文件的话),只有第三种方法不但没有多占质盘空间之忧,并且播放过程是交互式的,使我们能够非常方便地进行放音、暂停或停止等操作。因此,笔者在编程中比较偏爱使用第三种方法。

阅读全文(2635) | 回复(0) | 编辑 | 精华 | 删除
 


让三维动画在VFP表单中动起来
软件技术

fermos的博客 发表于 2005/9/8 7:57:30

让三维动画在VFP表单中动起来 http://www.pcdog.com 2004-10-29 互联网     首先,请将作出的动画(背景为白色)以BMP图象格式逐帧存放在所建项目的子目录下,
本例为title1.bmp,title2.bmp,......,title25.bmp.
    然后,在表单里增加一个图象类对象,名为Image1,将BackStyle属性改为 "透明";并在
其Picture属性中填入第一帧图象的文件名,本例为title1.bmp.
    接着,在增加一个计时器对象,在其Interval属性中填入100(每秒10帧,改动该值可
控制动画显示速度),并在Timer Event事件中填入如下代码:
    a=Thisform.Image1.Picture
    s=5 's为图象文件名前缀的英文字符个数,本例为5
    zs=25  'zs为图象的帧数,本例为25帧
    b=subst(a,Rat('',a)+1+s)
    n=Val(b)
    n=Iif(n=zs,1,n+1)
    Thisform.Image1.Picture=strtran(a,b,alt(str(n)+'.bmp')
   最后,按下"运行"按钮,图象动起来了.
   记住把所有bmp文件添加到项目管理器的"其他文件"里,这样在编译成exe文件时,VFP
会将所有BMP文件数据嵌入exe文件中并自动调用,运行时只需拷一个exe文件即可.

阅读全文(2098) | 回复(0) | 编辑 | 精华 | 删除
 


有趣的网络摄像头大揭露
电脑与网络

fermos的博客 发表于 2005/9/8 7:55:45

绝密隐私!有趣的网络摄像头大揭露 出处:电脑爱好者 [ 2005-06-20 09:34:40 ] 作者:飞翔 责任编辑:linjixiong ·盗版换正版,微软的免费午餐? ·新闻一周谈:微软发动"星球大战" ·终于来了!中国MSN服务全面启动 ·又遭泄漏,腾讯QQ2005 beta2曝光 ·10秒钟完成开机画面变脸! ·计算机锁定秘技:我的U盘变密钥 ·能和ACDSee一比高下的千千浏览 ·没有邀请也申请Gmail邮箱 ·Java咖啡馆:情人节的Applet ·Flash手绘一副帅气眼镜 ·商业三维动画短片制作流程揭秘 ·Photoshop消除照片中的黑眼圈 ·盗版换正版,微软的免费午餐? ·新闻一周谈:微软发动"星球大战" ·终于来了!中国MSN服务全面启动 ·又遭泄漏,腾讯QQ2005 beta2曝光 ·10秒钟完成开机画面变脸! ·计算机锁定秘技:我的U盘变密钥 ·能和ACDSee一比高下的千千浏览 ·没有邀请也申请Gmail邮箱 ·Java咖啡馆:情人节的Applet ·Flash手绘一副帅气眼镜 ·商业三维动画短片制作流程揭秘 ·Photoshop消除照片中的黑眼圈 var speed=100 demo2.innerHTML=demo1.innerHTML function Marquee(){ if(demo2.offsetTop-demo.scrollTop   现在,很多网友已经购买了好用又便宜的摄像头,可以和其他朋友进行视频聊天。不过,仅仅看到自己周围的人物有些单调,要是能看到世界各地的即时画面就好了。其实这并不难,Internet上有很多免费的摄像头(Webcam)等着你呢!

  利用Google搜索隐秘视频

  Google真的几近于无所不能,除可以用作强大的黑客工具外,还能用来搜索到成百上千未经加密的Webcam(网络摄像头)。只要在Google中搜索以下几个关键词中的任何一个:“inurl:"ViewerFrame?Mode="”,“inurl:"MultiCameraFrame?Mode="”,“intitlE:"Live View / - AXIS"”,“inurl:"axis-cgi/mjpg"”,“intext:"MOBOTIX M1" intext:"Open Menu"”或者“inurl:"view/index.shtml" ”等等,就可以发现非常多的在线视频(见图1)。不仅如此,已经有“乐于助人”者将这些结果都排列好了,大家可以直接到[这个页面]中查看。
图1  神秘的摄像头来自哪里?

  你可能会奇怪,这些视频都是哪里来的?为什么有人会把它们连接到互联网上?其实,这里也没有什么“不可告人”的秘密,这其中大部分都是安装在一些大厦顶端、公园、甚至家中的保安监视画面,只是连入互联网而已。这样,负责管理这些摄像头的用户就可以远程监控了。

  比如,在家里装上摄像头,上班时间就能在办公室查看家中状况,是否有小偷光顾等。这就好像现在国内一些大城市的主要路段都安装有摄像头,用来监控城市交通拥堵状况。因此从中并不能发现什么惊天的秘密,用来满足我们的好奇心或是看看外国的景色倒是不错。

  你知道吗?

  为什么Google的这些关键字能搜索到它们呢?

  其实原理很简单,这些设在各地的摄像机在购买的时候都会附赠一套软件,提供将其发布到Internet的服务,而不同品牌的摄像机产生的URL虽然不同,但都有一定规律。比如输入“inurl:"ViewerFrame?Mode="”后搜索到的就是Panasonic(松下)网络摄像机拍摄的画面,输入“image?cachebust=”搜索到的则是Camarades摄像机……

  快来看看几个有意思的Webcam站点

  1.日本某酒店的大堂,你可以通过页面左侧的按钮来指挥摄像机拍摄的角度,也可以设置画质、分辨率等,地址:http://lobby.yumemisaki.co.jp:8080/ViewerFrame?Mode=Motion。

  2.最受欢迎的几个Webcam,大都设置在会议室、网吧、停车场,打开该页面后选择“Show Most Popular”,单击“Go!”即可。地址:http://www.opentopia.com/hiddencam.php。

  3.该站点可以通过拍摄的场景和摄像机所在的国家来分类查找Webcam,比如你想看看美国某个风景区的即时画面就可以从这里找到。地址:http://www.onlinecamera.com/cgi-bin/find.cgi。
 4.这里提供的全部是美国各地的交通路况即时查看,不过对国内的朋友来说,看看玩还是不错,地址:http://www.pinecam.com。

  把Webcam设置成屏保

  笔者在寻找这些神秘的Webcam时,偶然发现了一款非常有趣的软件——V-CamShow,它内置了很多Webcam,包括伦敦的大笨钟(Big Ben)、尼亚加拉瀑布(Niagara Falls)、巴拿马运河(Panama Canal)等世界著名景点,你不仅可以观看它,还能像看幻灯片(Slide Show)一样欣赏每个画面。如果你兴致浓厚,干脆把Webcam设置成屏幕保护!

  V-CamShow小档案

  软件版本:1.4.0.0
  软件大小:460KB
  软件性质:免费软件
  运行平台:Windows 9X/ME/NT/2000/XP
  [点击下载]

  软件不需要安装,直接运行即可,在“Webcams”标签中选择一个你想观赏的地点,右边能够看到该Webcam的相关信息,单击“Webcam image URL”后面的“Connect to this cam”按钮即可看到远程画面了(见图2)。单击“Start Slide Show”按钮,你可以以默认10秒的画面间隔时间来全屏欣赏远程画面。切换到“Slide Show”标签,在“Slideshow interval”中可以设置刷新画面的时间间隔,勾选“Actual size (in window)”则在窗口中观看(见图3)。

图2  
图3  小提示

  如果你访问网络需要代理,则切换到“Webcam options”标签,勾选“Use proxy”并填写代理服务器的地址和端口号,“Http timeout (secs)”则填写连接超时的间隔时间。

  怎么样?Webcam还是挺有意思的吧?其实你也可以把自己的摄像头画面发布到网页上去,而且使用方法并非你想像的那么复杂。

阅读全文(2956) | 回复(0) | 编辑 | 精华 | 删除
 


在VFP中编程自动产生所输入汉字的拼音
软件技术

fermos的博客 发表于 2005/9/8 7:54:55

在VFP中编程自动产生所输入汉字的拼音 http://www.pcdog.com 2004-10-31 互联网   我们在编写数据库应用软件时,常常需要设计对人名、地名等各种汉字信息进行查找的功能。如果只允许用户输入汉字来查找,会使得查找功能不灵活,不完善;但如果允许用户输入汉字的拼音作为查找条件,除了多设一个储存汉字拼音的字段外,最大的问题在于输入资料时还得额外输入汉字的拼音,大大增加了输入的工作量。能不能通过编程的手段在输入汉字资料时自动产生其对应的拼音,从而解决这个矛盾呢?答案是 肯定的,下面介绍的方法就能让您轻松实现这个目的,为简单叙述起见,我们假设只需要产生汉字的声母,而不要韵母(无声母的则取韵母的第一个字母)。

---- 要自动产生拼音,首先必须有个汉字-拼音对照表。去哪里找这个表?自己动手可做一个。做表的方法较复杂,要先用工具生成汉字与拼音对应的文本文件,再用编程的手段将需要的内容加在数据表中。下面介绍一种具体的操作方法:

---- 第一步,利用“Windows 95附件”中“输入法生成器”的逆转化功能,将拼音输入法的码表文件“c:\windows\system\winpy.mb”逆转换为文本文件“c:\winpy.txt”(当然转换后的文件任您取名和指定位置)。用写字板打开此文本文件,可以看到这样的文字:

[Description]
Name=全拼
MaxCodes=12
MaxElement=1
UsedCodes=abcdefghijklmnopqrstuvwxyz
WildChar=?
NumRules=3
[Rule]
ca4=p10+p20+p30+p40
ce2=p10+p20
ce3=p10+p20+p30
[Text]
啊a
阿a
呵a
吖a
嗄a
腌a
锕a
阿爸aba
阿昌achang

---- 显然,最前面12行此时对于我们来讲是完全无用的,可用手工将之删除。另外,文件中还有大量的词的编码,如“阿爸aba”、“阿昌achang”等,对我们也是无用的,如何将之去掉使得文件中只保留单字及其编码呢?用手工显然太麻烦,只能靠一段小程序了。我们这样考虑,如果某行是单字及其编码,则这行的第三个字符肯定是字母,可以取这行的前三个字符存放在另一个文件中;而如果某行是词,则第三个字符是汉字的一部分,其asc值在128以上,我们就不用管它。
---- 下面给出这段小程序:

newfile=Fcreate('c:\py.txt')
&&创建文件,用于储存单字及其编码的文本
oldfile=Fopen('c:\winpy.txt') && 打开文件
=Fseek(oldfile, 0) && 移动指针到文件头
Do while not feof(oldfile)
c=left(Fgets(oldfile),3)
if asc(right(c,1))< 128 then
cc=chr(34)-left(c,2)-chr(34)-","-chr(34)-right(c,1)-chr(34)
=FPUTS(newfile, cc) &&以"字","z" 这样的格式写在新文件里
endif
Enddo
= Fclose(newfile) && 关闭文件
= Fclose(oldfile) && 关闭文件
creat table py (汉字 c(2),拼音 c(1)) &&创建新表
use py &&打开此表
append from c:\py.txt type delimited &&从文本中追加数据
use


---- 运行这段程序后,一个存有汉字和拼音首个字母的表PY就产生了。将这个表加入到我们的数据库中,并根据汉字建立索引,就可以使用这个表了。
---- 接下来要解决的问题是:输入汉字后,如何将之转换为拼音呢?假设在一个表单里已经建立了一个名为“汉字”的文本框用于输入汉字和一个名为“拼音”的文本框用于显示汉字对应的拼音。我们可在“汉字”这个文本框的LostFoucs事件中写入如下代码:

local a,b,c
c=""
b=""
a=thisform.汉字.value
select py &&假设此表在此之前已经打开且已经指定索引
for i=2 to len(a) step 2
store right(left((a),i),2) to b
seek b
store c-py.拼音 to c
next i
thisform.拼音.value=c

---- 至此,自动产生拼音的工作可以说成功完成了。至于如何添加在你的资料表上,就不必罗嗦了。这个方法为我们设计数据库应用软件带来了极大的好处,希望对大家有点启发作用。

阅读全文(1677) | 回复(0) | 编辑 | 精华 | 删除
 


VFP智能感应的二次开发
软件技术

fermos的博客 发表于 2005/9/8 7:53:09

VFP智能感应的二次开发 http://www.pcdog.com 2004-12-1 PCdog收集 下一页 1 2 
  摘要:本文提出了VFP内置编辑器的智能感应功能及用法,供智能感应使用的表FOXCODE.DBF的结构及功能。重点介绍了智能感应的二次开发功能的脚本程序编写方法。文中给出了几个实用的实例。   关键字:VFP 编辑器 智能感应   1 智能感应简介   VFP从7.0版开始增加了一个非常好的功能:智能感应( IntelliSense)功能。主要的功能有:(1)输入命令缩写及空格后,命令会自动扩展;(2)输入函数名及左括弧、对象名及点之后,系统会自动提示相应的参数或属性、方法名等供选择。除了上述这些与其它语言类似的功能以外,VFP还有一个特点,那就是它提供了可供用户二次开发的功能,即可以用脚本程序对功能进行扩展。    智能感应的所有内容及方案都在一个表中定义,表的文件名在系统变量_FOXCODE中存放。修改或扩展这个表的内容就可以改变智能感应的内容。在工具菜单中有一项是智能感应管理器(IntelliSense Manager)。智能感应管理器的作用其实就是帮助修改这个表。当然也可以直接用手工对表进行修改。    2 FOXCODE的表结构及用法   2.1 FOXCODE的表结构   此表共有十几个字段,下面介绍一下几个关键字段的用处:   Type:类型,有C(命令)、F(函数)、P(属性)、S(脚本)、O(对象)、U(用户自定义)等
  Abbrev:引发智能感应的关键词,也是命令或函数的最短缩写
  Cmd:填写脚本的名称,放在{}内,此脚本名应在另外一条记录的Abbrev字段中
  Data:备注型,存放脚本程序,或存放任意数据
  Expanded:命令或函数展开的全名
  Tip:提示信息   根据Type的不同,这些字段的意义也有所区别,而且不一定全部需要,不需要的字段可以空着,下面简单介绍一下不同用途的组合方式:   C或F:Abbrev中存放缩写,Expand中存放全称,Cmd中的脚本名可选
  S:Abbrev中存放脚本名,Data中存放脚本程序
  U:Abbrev中存放关键词,Cmd存放空括弧{},脚本直接存放在Data中;或者:Cmd中存放脚本名,Data中存放数据,脚本则在另外一条记录中定义。   2.2 智能感应的简单应用:提示信息的汉化   命令、函数、属性等类型的记录都是系统内部已经定义好的,虽然也可以修改,但基本上不好作太大的变动,毕竟功能的改造是有限的,在此就不详加叙述了。然而有一点工作是可以做的,那就是对Tip字段进行汉化。由于VFP从7.0开始已经不再出中文版了,汉化工作必须由自己做,对于智能感应提示的汉化,不需对二进制文件进行修改,直接修改表的Tip字段即可,这样汉化难度就很简单了。汉化时要注意,由于里面的逗号是作为参数分隔符的,不要进行增删,否则参数会错位。内容中若需要添加逗号,请使用全角的中文逗号。这样经过汉化之后,提示就变成中文的了。由于系统变量_FOXCODE的值可以改变,因此可以随时改变它的值,选择另外的表。若结合键激活功能,可以设置一个热键用于切换_FOXCODE的值,以便随时改变中、英文提示。   3.用户自定义类型的使用   用户自定义类型(Type为U)的记录,是我们可以添加的。下面介绍一下脚本编写的方法。   脚本实际上就是标准的VFP程序,简单地说,就是在编辑时输入一个在Abbrev字段中存在的字符串,并按空格键激活智能感应,那么系统就执行相应的脚本程序,用脚本的返回值替换输入的内容,若返回值为空,则不替换。调用脚本时,系统会传入一个对象型的参数FOXCODE,这个参数包含了用户输入的及FOXCODE表中相应记录中的许多有用的信息,脚本程序可以从中获取信息。因此不管是否需要这个对象,脚本程序的第一句必须用LPARAMETERS接收传入的参数。   4. FOXCODE对象的属性   FOXCODE对象有许多属性,没有方法。与FOXCODE表的字段同名的属性,分别代表相应的字段内容,除此以外比较有用的还有:   CursorLocaChar:执行脚本后,放在要选中文本内容前后的特殊字符,默认为“~”
  DefaultCase:缺省大小写,来自Type为V的记录的Case字段内容
  FileName:编辑的文件名
  FullName:当前输入的整行文本
  Location:使用编辑器的类型:
  0:命令窗口,1:程序,8:菜单片段,10:代码片段,12:存储过程
  UserTyped:用户输入的文本,不包括激活键、前导空格或Tab(可用FullLine代替)
  valueTip:当valueType为“T”时的提示
  valueType:脚本处理的方案,L:显示列表项,V:显示值,T:提示。这个属性一般需要在脚本中赋值,用户自定义的一般设置为“V”
  5.实例分析   为了能说得更清楚,现举几个实例,并对其进行分析说明。下面的例子中的Type都为“U”,Abbrev为要替换的关键字。“U”类型相当与“C”类型(命令),是用空格键激活的。若是类的方法或函数,是用左括弧激活的,那么必须用“F”类型, 其余的与“U”类型的类似。   5.1直接替换,不用脚本   在Expanded中存放展开后的字符串即可,字段长度为26,不超过就行。而且前面的几个字符不必与Abbrev的相同。   5.2 用单条记录实现   脚本程序直接存放在当前记录的Data字段中,在Cmd字段中存放空括弧{}。例:关键词为DC,替换为DEFINE CLASS语句块的多行内容,最后光标停在“类名”的位置。脚本程序为: LPARAMETERS oFoxcode
IF oFoxcode.Location #1 &&如果不是程序窗口,则不变
RETURN ""
ENDIF
oFoxcode.valuetype = "V" &&此行必需
TEXT TO myvar NOSHOW
DEFINE CLASS ~类名~ AS Session OLEPUBLIC
PROCEDURE Init ENDPROC
ENDDEFINE
ENDTEXT
RETURN myvar   说明:   (1)若使用智能感应管理器,则点击Custom中的Script时,LPARAMETERS一行会自动添加。   (2)TEXT-ENDTEXT间的几行(包括空行)为返回的内容,替换后“类名”为选中状态,便于直接输入。若只需光标定位,不需选中,则只要一个“~”就行了,符号“~”由CursorLocaChar属性决定,是可变的。   5.3 单独定义脚本   若脚本程序有通用性,则可以把脚本写到单独的记录中,供其它项调用。定义脚本:Type为“S”,Abbrev存放脚本名,Data存放脚本程序,Cmd为空。调用:Type为“U”,Abbrev存放关键词,Data存放数据,Cmd存放要调用的脚本名(两边加{})。例:关键词为SQ与IQ,分别生成SQL的SELECT与INSERT语句,字段名由脚本生成,若当前已有表打开,则使用当前表,否则提示打开。 Type Abbrev Cmd Data
U SQ {fieldlist} SELECT <> FROM <>
WHERE ~条件~ INTO CURSOR
U IQ {fieldlist} INSERT INTO <> (<>)
value (~<>~)   S fieldlist 脚本程序   fieldlist脚本程序如下 LPARAMETER oFoxCode
oFoxcode.valuetype = "V"
IF EMPTY(DBF()) &&没打开表,则提示打开
USE ?
ENDIF
AFIELDS(AR)
FieldList=AR(1,1)
FOR I=2 TO ALEN(AR,1)
FieldList=FieldList+","+AR(I,1)
NEXT
DbfName=JUSTSTEM(DBF())
RETURN TEXTMERGE(oFoxCode.Data) &&直接替换DATA中两个变量的内容   脚本程序说明: TEXTMERGE()函数可以用变量的值一次性替换字符表达式里<<>>中的变量。   6 其它   系统在第一次运行时会把FOXCODE.DBF从安装目录下复制一份到WINDOWS下的应用数据目录下,再使用它。HOME()目录下会有相同的一份,到底用的是哪一份,以系统变量_FOXCODE为准,也可以重新设置。
可以通过设置_VFP.EditorOptions属性来打开或关闭智能感应功能,赋"lq"可分别关闭成员列表与快速提示。小写为关闭,大写为打开。   7.总结   智能感应虽然是7.0版才开始有的,比其它的编程软件晚,但一开始就有强大的功能,更有意义的是它的功能是表FOXCODE.DBF的内容来实现的,因此用户可以通过修改或增加表的记录来增加系统智能感应的功能,达到了二次开发的目的,这一点是任何其它语言所不能达到的。    VFP的这种特性给编程带来了方便,若充分利用其功能,可以做到事半功倍的效果。文中提到的程序与脚本,都已在VFP7.0版中调试成功。

阅读全文(1467) | 回复(0) | 编辑 | 精华 | 删除
 


« 1 2 3 4 5 6 7 8 »



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.094 second(s), page refreshed 144760400 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号