返回列表 发帖

OnEvent模式进阶教程

很多autoit的用户(包括一些老用户)都认为OnEvent模式是简单的把GUIGetMsg 替换成了GUICtrlSetOnEvent,因此也产生了OnEvent模式不适合复杂GUI的误解。本文将具体阐述OnEvent模式的高级应用和使用技巧。

一、OnEvent模式的优势

OnEvent模式和MessageLoop模式最大的区别在于一个是主动,一个是被动。前者只有产生了消息才会执行函数,而后者则是不断地获取直到得到消息,这样CPU占用上OnEvent模式有很大优势。

另外,OnEvent模式能在运行脚本的同时获取GUI事件消息,这样我们就能把非常耗时的代码放在主循环里运行,而在其他的自定义函数里处理GUI消息。

比如下面这段代码,程序一直在主循环里更新控件内容,与此同时程序还能响应GUI消息:
  1. #include <GuiConstants.au3>

  2. GUICreate("MyGUI", 372, 88, -1, -1, BitOR($WS_OVERLAPPEDWINDOW, $WS_CLIPSIBLINGS))

  3. $Progress_1 = GUICtrlCreateProgress(10, 10, 370, 20)
  4. $Button_2 = GUICtrlCreateButton("start", 10, 40, 60, 30)
  5. $Button_3 = GUICtrlCreateButton("pause", 80, 40, 60, 30)
  6. $Button_4 = GUICtrlCreateButton("exit", 150, 40, 70, 30)

  7. Opt("GUIOnEventMode", 1)

  8. Dim $start = 0, $ostart

  9. GUISetState()
  10. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  11. GUICtrlSetOnEvent($Button_2, "gui")
  12. GUICtrlSetOnEvent($Button_3, "gui")
  13. GUICtrlSetOnEvent($Button_4, "gui")

  14. While 1
  15.     If $start >= 0 Then
  16.         Sleep(50)
  17.         $start += 1
  18.         If $start > 100 Then $start = 0
  19.         If $start >= 0 Then
  20.             $ostart = $start
  21.             GUICtrlSetData($Progress_1, $start)
  22.         Else
  23.             ContinueLoop
  24.         EndIf
  25.     EndIf
  26. WEnd

  27. Exit

  28. Func gui()
  29.     Switch @GUI_CtrlId
  30.         Case $GUI_EVENT_CLOSE, $Button_4
  31.             Exit
  32.         Case $Button_2
  33.             $start = 0
  34.         Case $Button_3
  35.             If $start = -100 Then
  36.                 $start = $ostart
  37.             Else
  38.                 $start = -100
  39.             EndIf
  40.     EndSwitch
  41. EndFunc   ;==>gui
复制代码
如果用MessageLoop模式来实现相同的效果,不但代码复杂,对GUI消息的响应也相当迟钝。也就是说MessageLoop模式只能在一个时间里做一件事,而OnEvent模式则能在做同样的事的同时抽出手去处理GUI事件。

所以,对于一些操作非常耗时或者GUI非常复杂的脚本,我们应首选OnEvent模式。

二、宏的妙用

许多人都有一个误解,认为一个事件只能注册一个函数,因此一些人把脚本写完之后代码里到处都是Func 、EndFunc。实际上,autoit本就提供了@GUI_CTRLID、@GUI_WINHANDLE、@GUI_CTRLHANDLE这三个宏,合理利用宏能大大简化代码

事实上,帮助文件里就有过说明:

注意:使用同一个函数来响应多种事件是完全合法的,记住要灵活使用 @GUI_CTRLID 这个宏。比如说,您可以注册所有系统事件到同一个函数。


下面是一个例子:
  1. #include <GuiConstants.au3>

  2. GuiCreate("MyGUI", 340, 60,-1, -1 , BitOR($WS_OVERLAPPEDWINDOW, $WS_CLIPSIBLINGS))

  3. $Button_1 = GuiCtrlCreateButton("Button1", 10, 10, 100, 40)
  4. $Button_2 = GuiCtrlCreateButton("Button2", 120, 10, 100, 40)
  5. $Button_3 = GuiCtrlCreateButton("Button3", 230, 10, 100, 40)

  6. Opt("GUIOnEventMode", 1)


  7. GUISetState()

  8. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  9. GUICtrlSetOnEvent($Button_2, "gui")
  10. GUICtrlSetOnEvent($Button_3, "gui")
  11. GUICtrlSetOnEvent($Button_1, "gui")

  12. While 1
  13.     ;;;
  14. WEnd

  15. Exit

  16. Func gui()
  17.     Switch @GUI_CtrlId
  18.         Case $GUI_EVENT_CLOSE
  19.             Exit
  20.         Case $Button_2
  21.             MsgBox(0,"","按下了Button_2")
  22.         Case $Button_3
  23.             MsgBox(0,"","按下了Button_3")
  24.         Case $Button_1
  25.             MsgBox(0,"","按下了Button_1")
  26.     EndSwitch
  27. EndFunc   ;==>gui
复制代码
在上面的例子里,我将全部的事件都注册到一个函数里,然后根据@GUI_CTRLID来判断具体的事件,这样代码就简洁多了。

而在关闭多窗口时@GUI_WINHANDLE则会起到很大作用,下面有个例子:
  1. #include <GuiConstants.au3>
  2. Opt("GUIOnEventMode", 1)

  3. $main = GuiCreate("MyGUI", 340, 60,-1, -1 , BitOR($WS_OVERLAPPEDWINDOW, $WS_CLIPSIBLINGS))
  4. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")

  5. $Button_1 = GuiCtrlCreateButton("Button1", 10, 10, 100, 40)
  6. $Button_2 = GuiCtrlCreateButton("Button2", 120, 10, 100, 40)
  7. $Button_3 = GuiCtrlCreateButton("Button3", 230, 10, 100, 40)

  8. GUISetState()

  9. GuiCreate("子窗口1", 140, 60,10,100,-1,-1,$main)
  10. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  11. GUISetState()
  12. GuiCreate("子窗口2", 140, 60,160,100,-1,-1,$main)
  13. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  14. GUISetState()
  15. GuiCreate("子窗口3", 140, 60,300,100,-1,-1,$main)
  16. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  17. GUISetState()

  18. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  19. GUICtrlSetOnEvent($Button_2, "gui")
  20. GUICtrlSetOnEvent($Button_3, "gui")
  21. GUICtrlSetOnEvent($Button_1, "gui")

  22. While 1
  23.     ;;;
  24. WEnd

  25. Exit

  26. Func gui()
  27.     Switch @GUI_CtrlId
  28.         Case $GUI_EVENT_CLOSE
  29.             Switch @GUI_WINHANDLE
  30.                 Case $main
  31.                     Exit
  32.                 Case Else
  33.                     MsgBox(0,"","关闭窗口:"&WinGetTitle(@GUI_WINHANDLE))
  34.                     GUIDelete(@GUI_WINHANDLE)
  35.             EndSwitch
  36.         Case $Button_2
  37.             MsgBox(0,"","按下了Button_2")
  38.         Case $Button_3
  39.             MsgBox(0,"","按下了Button_3")
  40.         Case $Button_1
  41.             MsgBox(0,"","按下了Button_1")
  42.     EndSwitch
  43. EndFunc   ;==>gui
复制代码
我们可以看到,主窗口关闭时程序会直接退出,而其余窗口关闭时则会删掉关闭的窗口。这里有个技巧,就是对于非主窗口,我们可以在Case Else的情况下用GUIDelete(@GUI_WinHandle)直接处理窗口,而不需要为每个窗口都写一个关闭函数。

三、多窗口操作技巧

建立多窗口时两种选择,一是在脚本开头就建立好全部窗口,然后把多余的窗口隐藏起来,在需要时再将隐藏的窗口显示出来。二是只建立主窗口,在需要时才建立子窗口。前一种选择会多占用内存,但响应速度非常快,适合使用多个非常复杂的窗口的脚本;而后一种选择明显要节约内存,但临时建立窗口会花费一些时间,适合一些子窗口简单的脚本。

至于帮助文件里提到的GUISwitch函数,实际上很少会用到。

下面分别是两种选择的范例:
隐藏子窗口型:
  1. #include <GuiConstants.au3>
  2. Opt("GUIOnEventMode", 1)

  3. $main = GuiCreate("MyGUI", 340, 60,-1, -1 , BitOR($WS_OVERLAPPEDWINDOW, $WS_CLIPSIBLINGS))
  4. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")

  5. $Button_1 = GuiCtrlCreateButton("显示窗口1", 10, 10, 100, 40)
  6. $Button_2 = GuiCtrlCreateButton("显示窗口2", 120, 10, 100, 40)
  7. $Button_3 = GuiCtrlCreateButton("显示窗口3", 230, 10, 100, 40)

  8. GUISetState()

  9. $s1 = GuiCreate("子窗口1", 140, 60,10,100,-1,-1,$main)
  10. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  11. GUISetState(@SW_HIDE)
  12. $s2 = GuiCreate("子窗口2", 140, 60,160,100,-1,-1,$main)
  13. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  14. GUISetState(@SW_HIDE)
  15. $s3 = GuiCreate("子窗口3", 140, 60,300,100,-1,-1,$main)
  16. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  17. GUISetState(@SW_HIDE)

  18. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  19. GUICtrlSetOnEvent($Button_2, "gui")
  20. GUICtrlSetOnEvent($Button_3, "gui")
  21. GUICtrlSetOnEvent($Button_1, "gui")

  22. While 1
  23.     ;;;
  24. WEnd

  25. Exit

  26. Func gui()
  27.     Switch @GUI_CtrlId
  28.         Case $GUI_EVENT_CLOSE
  29.             Switch @GUI_WINHANDLE
  30.                 Case $main
  31.                     Exit
  32.                 Case Else
  33.                     GUISetState(@SW_HIDE,@GUI_WINHANDLE)
  34.             EndSwitch
  35.         Case $Button_2
  36.             GUISetState(@SW_SHOW,$s2)
  37.         Case $Button_3
  38.             GUISetState(@SW_SHOW,$s3)
  39.         Case $Button_1
  40.             GUISetState(@SW_SHOW,$s1)
  41.     EndSwitch
  42. EndFunc   ;==>gui
复制代码
删除子窗口型:
  1. #include <GuiConstants.au3>
  2. Opt("GUIOnEventMode", 1)

  3. $main = GuiCreate("MyGUI", 340, 60,-1, -1 , BitOR($WS_OVERLAPPEDWINDOW, $WS_CLIPSIBLINGS))
  4. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")

  5. $Button_1 = GuiCtrlCreateButton("显示窗口1", 10, 10, 100, 40)
  6. $Button_2 = GuiCtrlCreateButton("显示窗口2", 120, 10, 100, 40)
  7. $Button_3 = GuiCtrlCreateButton("显示窗口3", 230, 10, 100, 40)

  8. GUISetState()
  9. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")

  10. GUICtrlSetOnEvent($Button_2, "gui")
  11. GUICtrlSetOnEvent($Button_3, "gui")
  12. GUICtrlSetOnEvent($Button_1, "gui")

  13. While 1
  14.     ;;;
  15. WEnd

  16. Exit

  17. Func gui()
  18.     Switch @GUI_CtrlId
  19.         Case $GUI_EVENT_CLOSE
  20.             Switch @GUI_WINHANDLE
  21.                 Case $main
  22.                     Exit
  23.                 Case Else
  24.                     GUIDelete(@GUI_WINHANDLE)
  25.             EndSwitch
  26.         Case $Button_2
  27.             if WinExists("子窗口2") = 0 Then
  28.                 GuiCreate("子窗口2", 140, 60,10,100,-1,-1,$main)
  29.                 GUISetState()
  30.                 GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  31.             EndIf
  32.         Case $Button_3
  33.             if WinExists("子窗口3") = 0 Then
  34.                 GuiCreate("子窗口3", 140, 60,160,100,-1,-1,$main)
  35.                 GUISetState()
  36.                 GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  37.             EndIf
  38.         Case $Button_1
  39.             if WinExists("子窗口1") = 0 Then
  40.                 GuiCreate("子窗口1", 140, 60,300,100,-1,-1,$main)
  41.                 GUISetState()
  42.                 GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  43.             EndIf
  44.     EndSwitch
  45. EndFunc   ;==>gui
复制代码
四、一些技巧

除去上面几节提到的一些,OnEvent模式下还有几项实用的技巧:

1.可控下载

在下载时,我们可以把下载函数放到主循环里进行,再根据某个变量的变化来停止、开始下载。而我们可以根据不同的GUI事件来控制这个变量。下面是一个例子:
  1. #include <GuiConstants.au3>
  2. Opt("GUIOnEventMode", 1)
  3. Dim $start,$stop

  4. GuiCreate("MyGUI", 392, 84,-1, -1 , BitOR($WS_OVERLAPPEDWINDOW, $WS_CLIPSIBLINGS))

  5. $Input_1 = GuiCtrlCreateInput("http://www.geocities.com/z8519312/ComicsDown-203.zip", 10, 10, 280, 30)
  6. $Button_2 = GuiCtrlCreateButton("下载", 300, 10, 80, 30)
  7. $Progress_3 = GuiCtrlCreateProgress(10, 50, 370, 20)

  8. GUISetOnEvent($GUI_EVENT_CLOSE, "gui")
  9. GUICtrlSetOnEvent($Button_2,"gui")

  10. GuiSetState()

  11. While 1
  12.     If $start=1 Then
  13.         GUICtrlSetData($Button_2,"停止")
  14.         $url = GUICtrlRead($Input_1)
  15.         $size = InetGetSize($url)
  16.         InetGet ($url, @TempDir&"\d.tem",0,1)
  17.         Do
  18.             Sleep(500)
  19.             GUICtrlSetData($Progress_3,@InetGetBytesRead /$size*100)
  20.             If $stop = 1 Then
  21.                 InetGet ("abort")
  22.                 ExitLoop
  23.             EndIf
  24.         Until @InetGetActive = 0
  25.         GUICtrlSetData($Button_2,"下载")
  26.         $stop = 0
  27.         $start = 0
  28.     EndIf
  29. WEnd

  30. Exit

  31. Func gui()
  32.     Switch @GUI_CtrlId
  33.         Case $GUI_EVENT_CLOSE
  34.             Exit
  35.         Case $Button_2
  36.             If GUICtrlRead($Button_2) = "下载" Then
  37.                 $start =1
  38.             Else
  39.                 $stop = 1
  40.             EndIf
  41.     EndSwitch
  42. EndFunc
复制代码
2.用数组储存控件ID

有时候我们需要注册的大量控件,如果是每一个控件都用GUICtrlSetOnEvent来注册,代码量会非常大,这时用数组就会省事得多。下面是个例子:
  1. Dim $Button[3]

  2. $Button[0] = GuiCtrlCreateButton("Button1", 10, 10, 100, 40)
  3. $Button[1] = GuiCtrlCreateButton("Button2", 120, 10, 100, 40)
  4. $Button[2] = GuiCtrlCreateButton("Button3", 230, 10, 100, 40)

  5. For $i =0 To 2
  6. GUICtrlSetOnEvent($Button[$i], "gui")
  7. Next
复制代码
这里有个问题,绝大多数人都使用KODA来建立窗口(其实我自己用的是GUIBuilder),而KODA不能以数组来命名控件ID,只能用button_1、button_2之类的名称来命名控件ID,这时我们就可以用Eval函数来解决:
  1. $Button_1 = GuiCtrlCreateButton("Button1", 10, 10, 100, 40)
  2. $Button_2 = GuiCtrlCreateButton("Button2", 120, 10, 100, 40)
  3. $Button_3 = GuiCtrlCreateButton("Button3", 230, 10, 100, 40)

  4. For $i =0 To 2
  5. GUICtrlSetOnEvent(Eval("Button_"&String($i)), "gui")
  6. Next
复制代码
——END——
15

评分人数

  • lixiaolong

  • xiehuahere

  • xms77

  • 虫子樱桃

  • Joo

这么好的贴没人顶呢?呵呵~!学习了,

TOP

谢谢了,要好好研究哈

TOP

这个帖子要顶。

TOP

谢谢!!谢谢!!

TOP

好东西。GUISetOnEvent

TOP

谢谢,收藏了

TOP

好好学习 天天向上

TOP

好好学习。。。

TOP

不知道要学多久才有LZ到境界!!
  努力学习中!!

TOP

好好学习,天天向上.

TOP

好帖子啊,楼主,多出一些这样的教程

TOP

很实用,谢谢分享。。。

TOP

精华!讲得实在太好了,学习了!

TOP

真是好帖,支持下

TOP

返回列表