Jump to content


Photo

GuiControl doesn't work for Static Gui Variables


  • Please log in to reply
16 replies to this topic

#1 Peter

Peter
  • Members
  • 448 posts

Posted 05 May 2007 - 10:48 AM

In AHK v1.0.46.01+, Gui can use static variables, but GuiControl doesn't seem to work then. (Tested on AHK v1.0.46.07 and v1.0.46.14)
Maybe it's meant to be like that, but I can't find it mentioned in AHK Help.
GuiControlVar()
Return
GuiControlVar()
{
   Static UserGroup  ; Only when this Gui variable is Global, GuiControl works OK

   guiRA= 1
   Gui, %guiRA%:add, DDL, vUserGroup w200, V%A_AhkVersion%||
   GuiControl,%guiRA%:, UserGroup, |Administrators||Backup Operators|Power Users|Users
   Gui, %guiRA%:show,
Return
GuiClose:
   ExitApp
Return
}


#2 Chris

Chris
  • Administrators
  • 10727 posts

Posted 05 May 2007 - 03:04 PM

The problem also extends to ByRef variables. It will be fixed in the next release and the changelog will say: Fixed GuiControl, GuiControlGet, and "Gui ListView/TreeView" to support static variables and ByRef's that point to globals/statics.

Thanks for reporting it.

#3 Peter

Peter
  • Members
  • 448 posts

Posted 04 July 2007 - 05:24 PM

The previous bug is solved, but I'm not sure about the behaviour under a label in Function.
GuiControl under the label doesn't work.
Is this a known limitation or some bug as well? (Tested in AHK V1.0.47.00)
GuiControlVar()
Return
GuiControlVar()
{
   Static guiRA
   Static UserGroup  ; when making UserGroup global, GuiControl in TestLabel works OK

   guiRA= 1
   Gui, %guiRA%:add, DDL, vUserGroup w200, V%A_AhkVersion%||
   Gui, %guiRA%:show,
   GuiControl,%guiRA%:, UserGroup, |Administrators||Backup Operators|Power Users|Users
   ; Here works "GuiControl" OK, but in TestLabel Timer routine it doesn't.
   SetTimer, TestLabel ,1000
   Return
TestLabel:
   SetTimer, TestLabel, off
   GuiControl,%guiRA%:, UserGroup, |TestLabel||
Return
GuiClose:
   ExitApp
Return
}


#4 PhiLho

PhiLho
  • Fellows
  • 6850 posts

Posted 05 July 2007 - 11:22 AM

SetTimer doesn't work well in functions, because the function was exited when the timer goes off, so context (local variables) is lost.
I believe this has been discussed before, so I searched. I found at least two related topics, started by yourself!
Array%index% doesn't work in label inside a function
Crash with SysGet in label inside function

#5 Peter

Peter
  • Members
  • 448 posts

Posted 05 July 2007 - 05:23 PM

I believe this has been discussed before, so I searched. I found at least two related topics, started by yourself!

That's right, I use labels in a function a lot, because I avoid global variables whenever possible.
However, ordinary Static variables are always accessible (e.g. guiRA in my code).
So I don't know it's a bug or a limitation for Static variables in Gui :? .
I can always use my old method again, but it's a bit tricky method, so I prefer Static variables for Gui.
(BTW, I don't see any problem using SetTimer in a function. I use it many times wihout any problem.)

#6 Chris

Chris
  • Administrators
  • 10727 posts

Posted 06 July 2007 - 12:51 AM

This behavior is unexpected; but it turns out there's two reasons for it:
1) The current architecture has no means of supporting static variables for GUI controls unless the function was actually called (not entered via a subroutine like SetTimer).
2) Performance: Keeping this limitation speeds up GUI variables when they're looked up outside of a function.

Due to obscurity, I really don't want this in the documentation. I checked both spots where it could be mentioned, but it would really make a mess of the already-complicated wording.

#7 corrupt

corrupt
  • Members
  • 2558 posts

Posted 06 July 2007 - 03:14 AM

Due to obscurity, I really don't want this in the documentation. I checked both spots where it could be mentioned, but it would really make a mess of the already-complicated wording.

:lol: Grab yer broom... it's goin' under the carpet with the rest of the mess...

#8 Chris

Chris
  • Administrators
  • 10727 posts

Posted 06 July 2007 - 12:57 PM

Supporting timers and other externally-called subroutines inside functions was never intended. At the time it was discovered, I was tempted to disable it but decided to keep it because some people said they were using it. Unfortunately, that feature is limited; certain types of extensions (like the one in this topic) can't be added to it without redesigning certain key data structures, which would increase memory load across-the-board (even in scripts that don't use the feature).

#9 corrupt

corrupt
  • Members
  • 2558 posts

Posted 06 July 2007 - 02:36 PM

Understood, but intentionally leaving gotcha information out of the documentation seems unprofessional IMHO... Maybe users here should start a new manual that contains an alphabetical list of gotchas and things that you don't want to add to the documentation?

#10 Chris

Chris
  • Administrators
  • 10727 posts

Posted 06 July 2007 - 02:53 PM

By all means. My rule-of-thumb is that if 99.9% of readers would want their time back after reading something, including it would do more harm than good.

#11 engunneer

engunneer
  • Fellows
  • 9162 posts

Posted 06 July 2007 - 03:45 PM

corrupt,

You should start a additional documentation page on the official wiki. Title it Gotchas if you want, but I think a better name would be, well, better.

#12 Peter

Peter
  • Members
  • 448 posts

Posted 06 July 2007 - 04:41 PM

Supporting timers and other externally-called subroutines inside functions was never intended. At the time it was discovered, I was tempted to disable it but decided to keep it because some people said they were using it.

Yes, thank you for keeping that, because I use it quite often :) .
But my example was maybe not accurate pointing to the problem. Because the limitation in using Static Gui variables has it's effect on control labels as well. (Actually that was my inital script problem)
GuiControlVar()
Return
GuiControlVar()
{
   Static guiRA
   Static btnTest,chkTest  ; when making btnTest & chkTest global, GuiControl in TestLabel works OK

   guiRA= 1
   Gui, %guiRA%:add, button, vbtnTest gTestLabel w250, Change buttontext && enable checkbox
   Gui, %guiRA%:add, checkbox, vchkTest disabled w250, Checkbox
   Gui, %guiRA%:show,
   Return
TestLabel:
   GuiControl,%guiRA%:, btnTest, Text on button is changed!     ; this doesn't work
   GuiControl,%guiRA%:enable, chkTest      ; this doesn't work
   MsgBox,Remark: GuiControl is in the Function label "%A_thisLabel%", and does not support Static Gui Variables.
Return
GuiClose:
   ExitApp
Return
}
I'm not saying that you have to solve the probem, but I'm not sure the situation is that exceptional. Unless if 99.9% of the readers will never use Static Gui variables, which could be possible.

I agree with other users here, to make this information somewhere easy accessible. If there's e.g. a link to "exceptional limitations" in the manual, then it's up to the reader whether he/she wants to check it out.

#13 Chris

Chris
  • Administrators
  • 10727 posts

Posted 06 July 2007 - 05:09 PM

This situation isn't exceptional merely because of the rarity of using static variables for GUI controls. It's exceptional because the problem doesn't occur unless the GUI control labels (or timers) are also inside a function. So if you want to use static variables for GUI controls -- and those controls need timers or GUI event-driven subroutines -- you should put those subroutines outside of the function and have them call the function in a special mode (e.g. by passing a special value via parameter). This is because calling a function is the only way that the program is designed to allow access to static variables dynamically (which includes GUI variables because they are looked up dynamically).

I don't wish to mention this on the GUI page because it's already so long that most readers give up long before reaching the end. However, I've revised the wording on the Functions page to mention it:

A function may contain externally-called subroutines such as timers, GUI g-labels, and menu items. This is generally done to encapsulate them in a separate file for use with #Include, which prevents them from interfering with the script's auto-execute section. However, the following limitations apply:

Such subroutines should use only static and global variables (not locals) if their function is ever called normally. This is because a subroutine thread that interrupts a function-call thread (or vice versa) would be able to change the values of local variables seen by the interrupted thread. Furthermore, any time a function returns to its caller, all of its local variables are made blank to free their memory.

Such subroutines should use only global variables (not static variables) as GUI control variables.

When a function is entered by a subroutine thread, any references to dynamic variables made by that thread are treated as globals (including commands that create arrays).

Thanks for pointing out the extent of the problem.

#14 Peter

Peter
  • Members
  • 448 posts

Posted 06 July 2007 - 09:18 PM

So if you want to use static variables for GUI controls -- and those controls need timers or GUI event-driven subroutines -- you should put those subroutines outside of the function and have them call the function in a special mode (e.g. by passing a special value via parameter).

I took me some time before I understood what you meant. Actually it can be a useful workaround. In case somebody else can use clarification by an example, this is Chris workaround:
GuiControlVar()

Return

GuiControlVar(Label="")

{

   Static guiRA,btnTest,chkTest



   If (Label= "TestLabel1") {

      GuiControl,%guiRA%:, btnTest, Text on button is changed!

      GuiControl,%guiRA%:enable, chkTest

      Return

   }

   guiRA= 1

   Gui, %guiRA%:add, button, vbtnTest gTestLabel1 w250, Change buttontext && enable checkbox

   Gui, %guiRA%:add, checkbox, vchkTest disabled w250, Checkbox

   Gui, %guiRA%:show,

   Return

GuiClose:

   ExitApp

Return

}

TestLabel1:    ; Remark: All these labels can be inside the function as well, that works fine too

TestLabel2:    ; just add the control-labels you need, and add another If-routine in the script.

TestLabeln:

   GuiControlVar(A_thisLabel)

Return


#15 Chris

Chris
  • Administrators
  • 10727 posts

Posted 07 July 2007 - 12:47 AM

Thanks for the example and the comments it contains.