马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本帖最后由 牢固 于 2013-5-4 16:23 编辑
我借用OpenDcl自带的实例Selections.lsp来介绍一下模态对话框和CAD之间交互应用的技巧和方法,先看下这个应用程序的演示:
该程序演示了模态对话框打开后,点击选择按钮,然后对话框关闭,在CAD屏幕进行相关操作,然后又如何返回到对话框的运行机制。下面我逐步介绍一下:
首先,加载对话框:
[pcode=lisp,true] ;; 加载OpenDcl
(setq cmdecho (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
(command "_OPENDCL")
(setvar "CMDECHO" cmdecho)
;; 加载Selections.odcl"对话框
(dcl_Project_Load (FindFile "Selections.odcl") t)
[/pcode]然后建立一个循环可使对话框能够重复加载
[pcode=lisp,true](setq doContinue T) ;_ 设置对话框可循环加载标志
(while doContinue
;; 为避免进入死循环,首先将循环标志doContinue 设为nil,是否继续循环,由对话框的按钮事件来控制
(setq doContinue nil)
;;显示对话框,当对话框关闭时,该句代码对话框将返回一个值给变量intResult ,该变量反应对话框时如何关闭的
;; 返回值=1表示对话框正常关闭,2表示用 ESC 关闭,其它返回值可由 dcl_form_close函数指定,
;;例如 (dcl_Form_Close Selections_Form 3)
(setq intResult (dcl_form_show Selections_Form))
;;因为这是一个模态对话框,所以程序将一直停留在 (dcl_form_show Selections_Form) 处,
;;直到对话框关闭,才会往下执行代码,程序的所有任务都要交给对话框中的事件来处理。
;;对话框关闭后,根据 intResult 的返回值,进行下一步处理
(cond
;; intResult = 1 ,正常关闭对话框,循环标志doContinue设为 nil,不在循环,
((= intResult 1) (setq doContinue nil)
;;此处可根据实际需要添加处理功能的代码
)
;; intResult = 2 , ESC 取消关闭对话框
((= intResult 2) (setq doContinue nil))
;; intResult = 3 , 关闭对话框,屏幕选点
((= intResult 3)
(point_selection) ;_ 执行屏幕选点子程序
)
;; intResult = 4 , 关闭对话框,屏幕选择对象
((= intResult 4)
(object_selection) ;_ 执行屏幕选择对象 子程序
)
); cond
); while
[/pcode]
对话框打开时,我们要把所有改变对话框各种控件属性状态的代码放在对话框的 OnInitialize 事件中:
[pcode=lisp,true] (defun c:Selections_Form_OnInitialize (/)
;; clean if necessary
(dcl_ListBox_Clear Selections_Form_PointListBox)
(dcl_ListBox_Clear Selections_Form_ObjectListBox)
;; fill pointlist
(if lstPoints
(dcl_ListBox_AddList Selections_Form_PointListBox lstPoints)
); if
;; fill objectlist
(if lstObjects
(dcl_ListBox_AddList Selections_Form_ObjectListBox lstObjects)
); if
(princ)
); c:Selections_Form_OnInitialize
[/pcode]
模态对话框打开后,所有事情都交给事件来处理,我们给屏幕选点图像命令按钮添加OnClicked事件:
[pcode=lisp,true] (defun c:Selections_Form_PickPointButton_OnClicked (/)
;;关闭对话框,并将数值 3 传递给dcl_form_show 处的变量intResult ,主程序根据intResult 的值,执行选点处理的函数
(dcl_form_close Selections_Form 3)
)
[/pcode]
选点处理的函数 :
[pcode=lisp,true](defun point_selection (/ intBlip ptPoint strPoint doSel)
;; 修改系统变量 BLIPMODE为1,以便选点时在屏幕上留下痕迹
(setq intBlip (getvar "BLIPMODE"))
(setvar "BLIPMODE" 1)
(setq doSel T) ;_ 设置循环选点标志
(while doSel ;_ 循环选点
;; 执行安全选点
(setq ptPoint (vl-catch-all-apply 'getpoint (list "\nPick a point (or press ENTER to return to the form): ")))
(cond
;; 用户 按下回车或右键,结束选择,设置对话框可循环加载标志doContinue 为真,继续重新加载对话框
((not ptPoint)
(setq ptPoint nil
doSel nil
doContinue T))
;; 用户按下ESC, 结束选择,设置对话框可循环加载标志 doContinue 为 nil,不再加载对话框
;; to cancel the loop and cancel the command
((vl-catch-all-error-p ptPoint)
(setq ptPoint nil
doSel nil
doContinue nil))
;; 将选择的点转为字符串
((not (setq strPoint (vl-prin1-to-string ptPoint)))
(setq ptPoint nil
doSel T)) ;_ 继续选择
;; 如果点表中不含选择的点,则将该点添加到点表中
((not (member strPoint lstPoints))
(setq lstPoints (reverse (cons strPoint (reverse lstPoints)))
doSel T)) ;_ 继续选择
); cond
); while
;; restore BLIPMODE
(setvar "BLIPMODE" intBlip) ;_ 恢复系统变量
)[/pcode]
同样,我们给选择对象按钮也添加一个OnClicked事件:
[pcode=lisp,true](defun c:Selections_Form_PickObjectButton_OnClicked (/)
;关闭对话框,并将数值 4 传递给 dcl_form_show 处的变量 intResult ,主程序根据 intResult 的值 ,执行选择对象处理的函数
( (dcl_form_close Selections_Form 4)
)
[/pcode]
选择对象处理的函数 :
[pcode=lisp,true]
(defun object_selection (/ intBlip ssAusw intLen entObj vlaObj strObj)
;; 修改系统变量 BLIPMODE为1,以便选对象时在屏幕上留下痕迹
(setq intBlip (getvar "BLIPMODE"))
(setvar "BLIPMODE" 1)
;; 执行安全选择对象
(princ "\nSelect objects (or press ENTER to return to the form): ")
(setq ssAusw (vl-catch-all-apply 'ssget nil))
(cond
;; 用户 按下回车或右键,没有选择对象, 设置对话框可循环加载标志 doContinue 为真,继续重新加载对话框
((not ssAusw)
(setq ssAusw nil
doContinue T))
;; 用户按下ESC, 结束选择, 设置对话框可循环加载标志 doContinue 为 nil,不再 加载对话框
((vl-catch-all-error-p ssAusw)
(setq ssAusw nil
doContinue nil))
;; 检查选择集中是否有对象
((zerop (setq intLen (sslength ssAusw)))
(setq ssAusw nil
doContinue T))
;; 如果选择的对象不在对象表lstObjects 中,则将对象添加到表中
(T (repeat intLen
(setq entObj (ssname ssAusw (setq intLen (1- intLen))))
(setq vlaObj (vlax-ename->vla-object entObj))
(setq strObj (strcat (vla-get-ObjectName vlaObj) " (" (vla-get-Handle vlaObj) ")"))
(if (not (member strObj lstObjects))
(setq lstObjects (reverse (cons strObj (reverse lstObjects))))
); if
); repeat
(setq doContinue T)) ;_ 继续加载对话框
); cond
;; 还原系统变量 BLIPMODE值
(setvar "BLIPMODE" intBlip)
)[/pcode]
该演示代码中还有ListBox的双击事件,删除选中项的代码示例:
[pcode=lisp,true] ;; 点列表双击事件,删除选中项
(defun c:Selections_Form_PointListBox_OnDblClicked (/ intRow)
(if (not (minusp (setq intRow (dcl_ListBox_GetCurSel Selections_Form_PointListBox))))
(progn
(setq lstPoints (vl-remove (dcl_ListBox_GetItemText Selections_Form_PointListBox intRow) lstPoints))
(dcl_ListBox_DeleteItem Selections_Form_PointListBox intRow)
); progn
); if
)
; 对象列表双击事件,删除选中项
(defun c:Selections_Form_ObjectListBox_OnDblClicked (/ intRow)
(if (not (minusp (setq intRow (dcl_ListBox_GetCurSel Selections_Form_ObjectListBox))))
(progn
(setq lstObjects (vl-remove (dcl_ListBox_GetItemText Selections_Form_ObjectListBox intRow) lstObjects))
(dcl_ListBox_DeleteItem Selections_Form_ObjectListBox intRow)
); progn
); if
)
[/pcode]
至此,该程序全部完成。完整的代码和对话框文件下载:
总结一下模态对话框重复加载的方法:
1、在主程序中建立循环,由循环标志来控制对话框的重复加载,这种方式和DCL的方法一致,所不同的是,OpenDcl重新加载对话框,OpenDcl会自动保留上次对话框的数据,而Dcl需要我们自己来设置对话框的初始值。
[pcode=lisp,true]...
(setq doContinue T) ;_ 设置循环加载对话框标志
(while doContinue
(setq doContinue nil) ;_ 禁止循环
(setq intResult (dcl_form_show Selections_Form)) ;_ 显示对话框
;;根据关闭对话框的返回值intResult分别处理
(cond
((= 1 intResult)
...
)
((= 2 intResult)
...
)
...
)
)
...[/pcode]
2、对话框打开后,所有的事情都要交给对话框的按钮事件来处理,直到对话框关闭,代码才会执行(dcl_form_show )后面的代码,牢记这点尤其重要,初学者往往因不理解OpenDcl这种机制,往往将上面的程序代码写成了如下方式:
[pcode=lisp,true];;错误的代码方式
(defun c:Sel ()
(vl-load-com)
(setq cmdecho (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
(command "_OPENDCL")
(setvar "CMDECHO" cmdecho)
;; 加载对话框
(dcl_Project_Load (FindFile "Selections.odcl") t)
;;显示对话框
(dcl_form_show Selections_Form)
;;关闭对话框事件
(defun c:Selections_Form_Close_OnClicked (/)
(dcl_form_close Selections_Form 1)
)
;;屏幕选点按钮事件
(defun c:Selections_Form_PickPointButton_OnClicked (/)
;;关闭对话框
(dcl_form_close Selections_Form )
;;执行屏幕选点
(point_selection)
;;选完点重新显示对话框
(dcl_form_show Selections_Form)
)
;; 屏幕选择对象按钮事件
(defun c:Selections_Form_PickObjectButton_OnClicked (/)
;;关闭对话框
(dcl_form_close Selections_Form )
;;执行屏幕选对象
(object_selection)
;;选完对象后重新显示对话框
(dcl_form_show Selections_Form)
)
)[/pcode]
这种写法就是因为不了解OpenDcl运行机制,才导致的错误!在OnClicked 事件中,(dcl_form_close Selections_Form ) 代码后面的(dcl_form_show Selections_Form) 代码根本不会执行,在OnClicked 事件中 关闭了对话框,其实对话框并没有正真结束,直到跳出 OnClicked 事件 才结束,所以后面的(dcl_form_show Selections_Form) 也不会执行!所以 dcl_Form_Show 显示模态对话框代码,代码的本质是显示窗体,暂时直至用户关闭对话框,正是基于这个理由,不要把用于即时改变或操作模态对话框的代码放在 dcl_Form_Show 函数的后面,相反,应把这些相关的代码放在对话框的 OnInitialize 事件中。不论对话框是模态还是非模态都需要这样做。
3、显示和隐藏窗体 当隐藏对话框时,用户应调用 dcl_Form_Close 来关闭它,而重新显示可用 dcl_Form_Show。所有控件的属性设置在对话框关闭后OpenDcl会自动保存下来,重新打开后对话框的数据依旧保存下来。
4、取消关闭对话框
用户可在应用程序中使用CancelClose事件来禁止用户关闭模态或非模态对话框。 这将防止所有通过常用途径关闭对话框的方法,如点击确定或取消键,或按下Esc键。
选择对话框后,在事件窗口中选定CancelClose项,添加该事件到你的LISP代码中。 以下代码可用于指定是否可以出现关闭。
[pcode=lisp,true]; ... (setq AllowClose T)
; AllowClose 是一个全局变量,用户可设置该事件来指示关闭对话框
; 该变量可被其它函数更改以标记OnCancelClose函数的需求动作。
; ...
(defun c:DclForm1_DclForm1_OnCancelClose (bCancelling / rCancelClose)
(setq rCancelClose nil)
; 在应用程序中跟踪用户设置以判断该对话框是否应该关闭。
(if (= AllowClose T)
; 设置返回变量以允许对话框关闭
(setq rCancelClose nil)
; 否则设置返回变量,不驱允许对话框关闭
(setq rCancelClose T)
)
; 变量放在这个位置以返回值给 OpenDCL
; 以通知应当取消关闭。
rCancelClose
)[/pcode] |