Debug Window, A AHK gui based console window

Post your working scripts, libraries and tools for AHK v1.1 and older
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Debug Window, A AHK gui based console window

16 Sep 2017, 00:26

Tired of using MsgBox to debug your stuff? tried the console ideas that acted weird? Here's a Pure AHK solution for a debug output!

Code: Select all

;DebugHelper.ahk
global DebugWindow_TextBox_Control
global hDebugWindow_TextBox_Control
global DebugWindow_

Class Debug
{
	static m_WindowOn := false
	static m_stack:=0
	static m_DebugLevel:=0
	MsgBox(Text)
	{
		MsgBox, 262144, Debug, %Text%
	}
	InitDebugWindow()
	{
		last:=a_defaultgui
		this.m_WindowOn := true
		this.m_stack := 0
		this.m_DebugLevel:=0
		this.m_MaxSize := 128*1024
		Gui, DebugWindow_:Default
		
		;allow resize
		Gui, +resize
		
		Gui, Color, 000000, 000022
		Gui, Font, q5 cAAAAAA s11,Courier New
		;Gui, Color, 000000, %DebugBackColor%
		;Gui, Font, c%DebugFontColor% w%DebugFontWeight% s%DebugFontSize%, %DebugFontName%

		Gui, Add, Edit, -Wrap +HScroll x0 y0 W400 H400 hwndhDebugWindow_TextBox_Control vDebugWindow_TextBox_Control 
		
		Gui, Show, , Debug Console
		WinSet, Top, , Debug Console
		Gui, %last%:Default
	}
	;basic write functions
	Write(Text, DebugLevel:=0)
	{
		if(DebugLevel<this.GetDebugLevel())
		{
			return
		}
		if(!this.m_WindowOn)
		{
			this.InitDebugWindow()
		}
		this._AppendText(hDebugWindow_TextBox_Control,Text)
	}
	WriteNL(Text, DebugLevel:=0)
	{
		if(DebugLevel<this.GetDebugLevel())
		{
			return
		}
		this.Write(Text . chr(13) . chr(10),DebugLevel)
	}
	WriteSP(Text, DebugLevel:=0)
	{
		if(DebugLevel<this.GetDebugLevel())
		{
			return
		}
		this.Write(Text . " ",DebugLevel)
	}
	WriteClear(Text, DebugLevel:=0)
	{
		if(DebugLevel<this.GetDebugLevel())
		{
			return
		}
		this._SetText(hDebugWindow_TextBox_Control,Text)
	}
	;The Max Size of the content, before the ouput is cleared
	GetMaxSize()
	{
		return this.m_MaxSize
	}
	SetMaxSize(MaxSize)
	{
		this.m_MaxSize := MaxSize
	}
	;DebugLevel, setting the debug level to 0,1,2,.... commands now take a Debug Level, 0 being default. 
	;the write calls will show if above or equal to the debug level
	;If you WriteNL at debug level 2 and the debug level is 1, meaning out every message of level 1 and above
	;the message will log to the output window
	;Debug Level 0 is considered System Level, log every call
	;One could have a debug level of 3 for useful things
	;2 for more information
	;1 for things like try catch failures
	;0 to log everything your program does
	SetDebugLevel(DebugLevel)
	{
		this.m_DebugLevel := DebugLevel
	}
	GetDebugLevel()
	{
		return this.m_DebugLevel
	}
	;Stack write function to indent/un-intend, each function returns the stack position if you need to reset after a break call for example
	GetStack()
	{
		return this.m_stack
	}
	SetStack(Level)
	{
		this.m_stack :=level
		return this.m_stack
	}
	ClearStack()
	{
		this.m_stack := 0
		return this.m_stack
	}
	IncStack()
	{
		this.m_stack ++
		return this.GetStack()
	}
	DecStack()
	{
		this.m_stack --
		return this.GetStack()
	}
	WriteStackPush(Text, DebugLevel:=0)
	{
		if(DebugLevel<this.GetDebugLevel())
		{
			return this.IncStack()
		}
		this.WriteStack(Text,DebugLevel)
		this.IncStack()
		return this.GetStack()
	}
	WriteStackPop(Text, DebugLevel:=0)
	{
		if(DebugLevel<this.GetDebugLevel())
		{
			return this.DecStack()
		}
		this.DecStack()
		this.WriteStack(Text)
		return this.GetStack()
	}
	WriteStackClear(Text, DebugLevel:=0)
	{
		if(DebugLevel<this.GetDebugLevel())
		{
			return this.ClearStack()
		}
		this.ClearStack()
		this.WriteStack(Text,DebugLevel)
		return this.GetStack()
	}
	WriteStackClearTo(Text,Level, DebugLevel:=0)
	{
		if(DebugLevel<this.GetDebugLevel())
		{
			return this.SetStack(Level)
		}
		this.SetStack(Level)
		this.WriteStack(Text,DebugLevel)
		return this.GetStack()
	}
	WriteStack(Text, DebugLevel:=0)
	{
		if(DebugLevel<this.GetDebugLevel())
		{
			return this.GetStack()
		}
		ct := this.m_stack
		loop, %ct%
		{
			this.Write("  ",DebugLevel)
		}
		this.WriteNL(Text,DebugLevel)
		return this.GetStack()
	}
	
	_AppendText(hEdit, Text) 
	{
		if(DllCall( "GetWindowTextLength", UInt, hEdit)>this.GetMaxSize())
		{
			this._SetText(hEdit,"")
		}
		
		SendMessage, 0x000E, 0, 0,, ahk_id %hEdit% ;WM_GETTEXTLENGTH
		SendMessage, 0x00B1, ErrorLevel, ErrorLevel,, ahk_id %hEdit% ;EM_SETSEL
		SendMessage, 0x00C2, False, &Text,, ahk_id %hEdit% ;EM_REPLACESEL
	}
	
	_SetText(hEdit,Text)
	{
		SendMessage, 0x000C, False, &Text,, ahk_id %hEdit% ;EM_SETTEX
	}
	CloseOutput()
	{
		last:=a_defaultgui
		Gui, DebugWindow_:Default
		Gui, Destroy
		Gui, %last%:Default	
		this.m_WindowOn := false
	}
	OnEnter()
	{
		last:=a_defaultgui
		Gui, DebugWindow_:Default
		GuiControlGet, DebugWindow_TextBox_Control
		LastText := ""
		
		LastText:=substr(DebugWindow_TextBox_Control,inStr(DebugWindow_TextBox_Control,"`n",,0)+1)

		DoCaret:=true
		;Output Window Commands
		if(LastText = ">Close")
		{
			DoCaret := false
			this.CloseOutput()
			
		}
		else if(LastText = ">Exit")
		{
			DoCaret := false
			ExitApp
		}
		else if(LastText = ">Clear")
		{
			DoCaret := false
			this.WriteClear("",100000)
		}
		else if(LastText = ">set debug level 0") ;I'll be lazy
		{
			this.SetDebugLevel(0)
			this.WriteNL("",100000)
			this.WriteNL("debug level -> 0",100000)
			
		}
		else if(LastText = ">set debug level 1") ;I'll be lazy
		{
			this.SetDebugLevel(1)
			this.WriteNL("",100000)
			this.WriteNL("debug level -> 1",100000)
			
		}
		else if(LastText = ">set debug level 2") ;I'll be lazy
		{
			this.SetDebugLevel(2)
			this.WriteNL("",100000)
			this.WriteNL("debug level -> 2",100000)
			
		}
		else if(LastText = ">set debug level 3") ;I'll be lazy
		{
			this.SetDebugLevel(3)
			this.WriteNL("",100000)
			this.WriteNL("debug level -> 3",100000)
			
		}
		else if(LastText = ">set debug level 4") ;I'll be lazy
		{
			this.SetDebugLevel(4)
			this.WriteNL("",100000)
			this.WriteNL("debug level -> 4",100000)
			
		}
		else if(LastText = ">set debug level 5") ;I'll be lazy
		{
			this.SetDebugLevel(5)
			this.WriteNL("",100000)
			this.WriteNL("debug level -> 5",100000)
			
		}
		else if(LastText = ">get debug level") ;I'll be lazy
		{
			this.WriteNL("",100000)
			this.WriteNL("debug level is " . this.GetDebugLevel(),100000)
			
		}
		if(DoCaret)
		{
			;Force the caret to be on the last line, add a > prompt. For commands, a first enter is needed to get to the prompt
			send, ^{end}{enter}>
		}
		Gui, %last%:Default	
	}
}

;Window Resize event
DebugWindow_GuiSize(GuiHwnd, EventInfo, Width, Height)
{
	; The window has been minimized.  No action needed.
	if(EventInfo <> 1)
	{
		last:=a_defaultgui
		Gui, DebugWindow_:Default
		GuiControl, Move, DebugWindow_TextBox_Control, % "H" . (Height) . " W" . (Width)
		Gui, %last%:Default	
	}
	return
}
DebugWindow_GuiClose()
{
	Debug.CloseOutput()
}
DebugWindow_GuiEscape()
{
	Debug.CloseOutput()
}


return
#IfWinActive , Debug Console
enter::
Debug.OnEnter()
return
Debug.MsgBox() is a wrapper arroung MsgBox with the proper flag so you SEE the darn thing even if a topmost window is about.
Debug.Write() Creates the Output Window, if it's not created, then pops it up adding content to the text box.
Debug.WriteNL() , Adds a New Line after the text
Debug.WriteSP() , Adds a space after the text
Debug.MsgBox("Var is: " . var)
Debug.WriteNL("Var is: " . var)

V3.0 Major Additions
Added Debug Level to turn on and off various levels of debug information
Added appropriate commands to the console, see the code
Added Text Limit to cause a Clear when the limit is reached
Added WriteStack set of functions which basically adds leading spaces so you can push a text at the beginning of a function and pop at the end
Added Functions to play with and reset the stack value if you need to, like if you break out of multiple loops... and you are tracing the loop
See example In Post Below

V2 Adds
Debug.WriteClear() To replace the entire content
GUIClose And GUIEscape Events, which close the window. It will pop again when Write() is called, but with new content
Write() will show (and activate) only the first time, since it's on top, subsequent write call only change the text (v2.2 removes the AlwaysOnTop)
Cleaned up some superfluous GUI default calls.
Removed the Edit Box gLabel
Added some console commands
Clear - to reset the text to nothing
Close - to close the window
Exit - to exit the app
Enter:: is used for this window, if it's active, to handle the enter event, since the output box is editable, that was the work around short of adding low level message handling

V2,1
Added
last:=a_defaultgui
Gui, last:Default

V2.2
Removed the Always On Top option
Fixed the send {enter} being performed at the wrong spot in the code
Added >Prompt on enter.
Made comparison for commands case insensitive
Added suggested find last line code and replaced the less efficient loop

V2.3
Bug Fix, Changed
Gui, last:Default
to
Gui, %last%:Default
Last edited by icuurd12b42 on 22 Sep 2017, 02:55, edited 8 times in total.
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 00:33

I don't get why you made the control variables global (or even bothered using control variables in the first place). Also resetting the default gui to Gui 1 after a method has been run is not a good idea.
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 01:41

RUNIE wrote:I don't get why you made the control variables global (or even bothered using control variables in the first place).
I'm hoping to catch edit box events later on to do thing in my project like a console window
Also resetting the default gui to Gui 1 after a method has been run is not a good idea.
Isn't 1 the default gui window id? Why is this not a good idea? Most gui code I've seen don't use multiple GUIs ids. without this The GUI context would switch to the debug window and people's UI stuff would stop working
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 02:36

You can change the default gui. It would be reset to 1 after running any methods from your class.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 03:25

RUNIE wrote:You can change the default gui. It would be reset to 1 after running any methods from your class.
Are you sure?

Code: Select all

#NoEnv
MsgBox, %A_DefaultGui% ; 1
Test.Method()
MsgBox, %A_DefaultGui% ; NewDefaultGui
Return

Class Test {
   Method() {
      Gui, NewDefaultGui: Default
   }
}
There's no need to change the default GUI within the methods.
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 04:23

just me wrote:Are you sure?
Yes..

Code: Select all

Gui 5: Default
msgbox % A_DefaultGui ; 5
debug.InitDebugWindow()
msgbox % A_DefaultGui ; 1
return
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 05:09

dude you're messing with my head I been playing with multiple gui for 2 months now...

Code: Select all

Gui 5: Default
msgbox % A_DefaultGui ; 5
debug.InitDebugWindow()
msgbox % A_DefaultGui ; 4 it's 4 not 1
return
class Debug
{
    InitDebugWindow()
    {
        Gui 4: Default
    }
}
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 05:36

icuurd12b42 wrote:dude you're messing with my head I been playing with multiple gui for 2 months now...
?? I'm using the code in your OP not some arbitrary code you just wrote to test.
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 05:55

>You can change the default gui. It would be reset to 1 after running any methods from your class.

That statement means that if you set the GUI's default within a function it resets upon the function's exit. Just Me... and myself saw that comment that way.

As for my extra calling of the Set Default and reset to 1 when done which just may not be required every call, I agree, I could be more cautious.
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 06:03

By that I mean you manually set the default Gui to 1. I really thought that was obvious considering the code in the OP.
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 08:05

Well your comments were a bit confusing, you have to admit.

I cleaned up the code and added the missing features and handling and I assure you all the GUI Default left are absolutely needed :) some where removed since I'm doing winapi calls on the text handle... The rest well, to use the ahk methods, they are needed
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 08:39

You should store a_defaultgui in variable before setting the defualt to the debug gui, then set it back to what you stored, this might or might not be 1, it could be 5 as shown by RUNIE.

Cheers and thanks for sharing :thumbup:
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 08:54

Yeah, I just added that thanks

Would have helped if it had been pointed out that way from the start, his statement implied setting to 1 was pointless because the language resets it to 1 upon a function exit. And it's not just me, but just me as well that saw it that way... and I love these non nonsensical statements from weird user names...
User avatar
boiler
Posts: 16768
Joined: 21 Dec 2014, 02:44

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 09:46

He did point it out that way from the start. He started by telling you that it's not a good idea for you to be changing the default GUI to 1, and you seemed to understand that's what he meant because you said you thought it was fine because most GUI code you've seen only uses one GUI anyway. If you want your code to only be compatible most of the time, that's your choice.

Not trying to drag out this debate, but I'm just pointing out that RUNIE gave you some feedback that really improved the usability of your code, and you seem to be criticizing him for it or for the fact that you didn't understand it. And my feedback here is to try to help you not discourage others from providing useful feedback to you in the future, so please take it as intended.
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 16:39

Yes, I already fixed everything and explained why I was confused and helgef finally pointed out what runie meant, we all good now.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Debug Window, A AHK gui based console window

16 Sep 2017, 17:04

Internet is the perfect place for misunderstandings...
For the hotkey, maybe use case insensitive (=) comparisons for Exit, Clear, etc commands. And maybe something like this to get the last line instead of the loop,

Code: Select all

lastLine:=substr(DebugWindow_TextBox_Control,inStr(DebugWindow_TextBox_Control,"`n",,0)+1)
where the 0 in instr says to start looking from the end.
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Debug Window, A AHK gui based console window

17 Sep 2017, 05:23

Done! and added a > prompt on Enter Pressed

Thanks all for the feedbak
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Debug Window, A AHK gui based console window

21 Sep 2017, 07:22

V2.3
Bug Fix, Changed
Gui, last:Default
to
Gui, %last%:Default
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Debug Window, A AHK gui based console window

22 Sep 2017, 02:58

V3.0 Major Additions
Added Debug Level to turn on and off various levels of debug information
Added appropriate commands to the console, see the code
Added Text Limit to cause a Clear when the limit is reached
Added WriteStack set of functions which basically adds leading spaces so you can push a text at the beginning of a function and pop at the end
Added Functions to play with and reset the stack value if you need to, like if you break out of multiple loops... and you are tracing the loop

Example WriteStack Result:

Code: Select all

ProcessMonitor.OnTimer() Window Change: 0x1065e
  Process Changed: 8760
  ------------------
  NEW WINDOW DETECTED
  hWnd: 67166
  Class: Notepad
  Title: Untitled - Notepad
  Process: notepad
  Process ID: 8760
  Path: C:\Windows\System32\notepad.exe
  ------------------
  ProcessMonitor.OnProcessChanged() Start
    ProcessMonitor.SelectTVProcess() Start
      recurse_groups enter
        while(ThisTVItem) top
          ITEM NAME: ApCent TYPE: 2
        while(ThisTVItem) bottom
        while(ThisTVItem) top
          ITEM NAME: charmap TYPE: 2
        while(ThisTVItem) bottom
        while(ThisTVItem) top
          ITEM NAME: chrome TYPE: 2
        while(ThisTVItem) bottom
        while(ThisTVItem) top
          ITEM NAME: explorer TYPE: 2
        while(ThisTVItem) bottom
        while(ThisTVItem) top
          ITEM NAME: Group TYPE: 5
          (TheType == TV_TYPES.GROUP) top
            recurse_groups enter
              while(ThisTVItem) top
                ITEM NAME: Group TYPE: 5
                (TheType == TV_TYPES.GROUP) top
                  recurse_groups enter
                    while(ThisTVItem) top
                      ITEM NAME: notepad TYPE: 2
                    while(ThisTVItem) bottom
                  recurse_groups leave
                (TheType == TY_TYPES.GROUP) bottom
              while(ThisTVItem) bottom
            recurse_groups leave
          (TheType == TY_TYPES.GROUP) bottom
        while(ThisTVItem) bottom
      recurse_groups leave
      For Each Exe Treeview Item
        ApCent == notepad?
        charmap == notepad?
        chrome == notepad?
        explorer == notepad?
        notepad == notepad?
        notepad == notepad! Break!
        
      End For
    ProcessMonitor.SelectTVProcess() End
  ProcessMonitor.OnProcessChanged() End
  ProcessMonitor.OnWindowChanged() Start
    ProcessMonitor.SelectTVWindowContext() Start
      recurse_groups enter
        while(ThisTVItem) top
          ITEM NAME: Command TYPE: 3
        while(ThisTVItem) bottom
        while(ThisTVItem) top
          ITEM NAME: Loading TYPE: 5
          (TheType == TV_TYPES.GROUP) top
            recurse_groups enter
              while(ThisTVItem) top
                ITEM NAME: Window2 TYPE: 6
              while(ThisTVItem) bottom
              while(ThisTVItem) top
                ITEM NAME: Window3 TYPE: 6
              while(ThisTVItem) bottom
            recurse_groups leave
          (TheType == TY_TYPES.GROUP) bottom
        while(ThisTVItem) bottom
        while(ThisTVItem) top
          ITEM NAME: Saving TYPE: 5
          (TheType == TV_TYPES.GROUP) top
            recurse_groups enter
              while(ThisTVItem) top
                ITEM NAME: Window2 TYPE: 6
              while(ThisTVItem) bottom
              while(ThisTVItem) top
                ITEM NAME: Window3 TYPE: 6
              while(ThisTVItem) bottom
            recurse_groups leave
          (TheType == TY_TYPES.GROUP) bottom
        while(ThisTVItem) bottom
        while(ThisTVItem) top
          ITEM NAME: Window TYPE: 6
        while(ThisTVItem) bottom
        while(ThisTVItem) top
          ITEM NAME: Window2 TYPE: 6
        while(ThisTVItem) bottom
      recurse_groups leave
      For Each Window Context Item Found
      Is RegExMatch On Class Name?
      }
      Is RegExMatch On Class Name?
      }
      Is RegExMatch On Class Name?
      }
      Is RegExMatch On Class Name?
      }
      Is RegExMatch On Class Name?
      }
      Is RegExMatch On Class Name?
        Yes RegEx Match on ClassName
          Is RegExMatch on Title?
          Yes RegEx Match on Title
            Is RegExMatch on Control Name?
            RegEx Match on Control Name
              Is RegExMatch on Control Tex?
              Yes RegEx Match on Control Text
              Found! Break!!!
      For Each End
    ProcessMonitor.SelectTVWindowContext() End
  ProcessMonitor.OnWindowChanged() End
ProcessMonitor.OnTimer() Window Change End
User avatar
jasc2v8
Posts: 59
Joined: 10 Dec 2020, 12:24
Contact:

Re: Debug Window, A AHK gui based console window

10 Dec 2020, 13:29

Nice Job!
Here is an example for us AHK newbies:

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

DebugWindowHelp=
(Join`r`n
Debug console commands are:
  >Clear
  >CloseOutput
  >Exit
  >set debug level 0
  >set debug level 1
  >set debug level 2
  >set debug level 3
  >set debug level 4
  >set debug level 5
  >get debug level
Press ENTER for the command prompt
)

D:=new Debug
D.InitDebugWindow()
D.WriteNL(DebugWindowHelp)
D.WriteNL("Example of writing text to the debug window")
MsgBox Enter commands in the debug console, then press OK to terminate
D.CloseOutput()
ExitApp

Enter::
D.OnEnter()

#Include DebugClass.ahk ; must be at bottom 


Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: gwarble, JoeWinograd and 134 guests