Jump to content


Photo

For those who want to know how a newb implemented OOP


  • Please log in to reply
4 replies to this topic

#1 plastikglass

plastikglass
  • Members
  • 93 posts

Posted 10 April 2012 - 08:49 PM

Hey guys, this is for some of the people asking from this thread, http://www.autohotke...=527644#p527644, about how I as a newb went about transforming my procedural-based program into an object-oriented one.

Before I begin, I just want to let you know that this is not a tutorial, but just a thread to explain what I did. I am not a great teacher nor do I claim to be of AHK_L objects. I hope this thread will interest others, and gain notice from the experts that a non-programmer was able to get somewhere with OOP in AHK_L, but would have gone farther if he received more support/better teaching materials. In any case, lets begin.

This is sample of my original work. This was when I first started out in AHK and programming in general.

DesktopReimage()
{
global ;StartTime
Critical, off
Userprompt()
if ErrorLevel
return
else
;StartTime := A_TickCount
Workorder()
Send, desktop rei{ENTER} ;Selects Desktop Reimage template
Sleep, 200
Division()
Timer()
Sleep 200
return
}

As you can see, there are four other functions inside of this function. The name of the main function was the ticket I needed to create in our help desk software. Each time I wanted to make a separate function for a ticket, I basically had to make a copy just like the one above to achieve that. This can be both time consuming and heavy in terms of scripting, and if I there was a single change to the way our tickets were made, I would have to make a change to every function!!! This alone made me consider objects. But before I get to my OOP version, here are the four internal functions that each ticket function must have:

Timer()
{
global ;StartTime
WinWait, ahk_id %workorder%, 
IfWinNotActive, ahk_id %workorder%, , WinActivate, ahk_id %workorder%, 
WinWaitActive, ahk_id %workorder%, 
Send {ALTDOWN}vtoo{ALTUP}{ENTER}
Sleep 200
Send {ALTDOWN}ooo{ALTUP}
SendRaw Look for the time prompt!
MsgBox,, %LISTBOX%, Press OK once you have finished resolving the ticket.
;MsgBox %StartTime%
;IfMsgbox Ok
	;EndTime := A_Now
;MsgBox %EndTime%
ElapsedTime := A_TickCount - StartTime
;Msgbox %ElapsedTime%
SetFormat, Float, 0.2
ElapsedTime /= 3600000.00
MsgBox,, Elapsed Time, %ElapsedTime%
Sleep 200
WinWait ahk_id %workorder%,,10
IfWinNotActive ahk_id %workorder%,,10
WinActivate ahk_id %workorder%,,10
WinWaitActive ahk_id %workorder%,,10
if ErrorLevel
{
    MsgBox, WinWait timed out.
    return
}
else
;Sleep 200
;Send, {ALTDOWN}vtoo{ALTUP}{ENTER}
;Sleep 200
;Send {ALTDOWN}ooo{ALTUP}
;SendRaw Look for the time prompt!
Sleep 200
Send {ALTDOWN}oo{ALTUP}
Send %ElapsedTime%
return
}


Division() ;Writes in the division and department of the user
{
global
if DROPDOWN = DSF
Send, {ALTDOWN}uu{ALTUP}d{ENTER}{ALTDOWN}m{ALTUP}d{ENTER} ;Selects DSFNT4 selection
else if DROPDOWN = OSFNS
Send, {ALTDOWN}uu{ALTUP}f{DOWN}{ENTER}{ALTDOWN}m{ALTUP}f{ENTER} ;Selects for OSFNS selection
else if DROPDOWN = CENTRAL
Send, {ALTDOWN}uu{ALTUP}c{ENTER}{ALTDOWN}m{ALTUP}c{ENTER} ;Selects for Central users selection
else if DROPDOWN = PUPIL TRANSPORT
Send, {ALTDOWN}uu{ALTUP}p{DOWN 2}{ENTER}{ALTDOWN}m{ALTUP}p{ENTER} ;Selects for PUPIL TRANSPORT selection
else if DROPDOWN = OPTDEV
Send, {ALTDOWN}uu{ALTUP}a{ENTER}{ALTDOWN}m{ALTUP}o{ENTER} ;Selects for OPTDEV settings
else if DROPDOWN = PSAL
Send, {ALTDOWN}uu{ALTUP}a{DOWN}{ENTER}{ALTDOWN}m{ALTUP}f{ENTER} ;Selects for PSAL selection
else
Send, {ALTDOWN}uu{ALTUP}f{ENTER}{ALTDOWN}m{ALTUP}f{ENTER} ;Selects for OSFNS Field users selection
Sleep 200
Send {ALTDOWN}ii{ALTUP}.{ENTER} ;To select NON-POS user
}

Workorder(pos = 0) ;Creates the work order and opens template selection
{
global
IfWinNotActive Track-It! Technician Client,,3  
WinActivate Track-It! Technician Client,,3
WinWaitActive Track-It! Technician Client,,3
if ErrorLevel
{
    MsgBox, WinWait timed out.
    return
}
else
Sleep, 200
Send, {CTRLDOWN}w{CTRLUP} ;Creates a new work order
WinWait, Work Order - New, ,,,Billing Information
;IfWinNotActive, Work Order - New, ,,,Look for the time prompt! 
WinActivate, Work Order - New, ,,,Billing Information
WinWaitActive, Work Order - New, ,,,Billing Information
Sleep 300
WinGet, workorder, ID, Work Order - New
WinWait ahk_id %workorder%,,10
IfWinNotActive ahk_id %workorder%,,10
WinActivate ahk_id %workorder%,,10
WinWaitActive ahk_id %workorder%,,10
if ErrorLevel
{
    MsgBox, WinWait timed out.
    return
}
else
Sleep, 200
if (pos = "pos")
{
Send, %name% {ENTER}
}
Sleep, 200
Send, {ALTDOWN}ams{ALTUP} ;Function to call up template selection
StartTime := A_TickCount
WinWait Apply Template,,3
IfWinNotActive Apply Template,,3
WinActivate Apply Template,,3
WinWaitActive Apply Template,,3
if ErrorLevel
{
    MsgBox, WinWait timed out.
    return
}
}


Userprompt() ;Begins the first prompt for user's name
{
global
WinWait, Track-It! Technician Client, ,3
if ErrorLevel
{
Run C:\Documents and Settings\%A_Username%\Start Menu\Programs\Numara Track-It!\Numara Track-It! Technician Client.appref-ms
;MsgBox, TrackIt! is not open.
return
}
else if DROPDOWN = DSF
{
loop
{
InputBox, name, Enter Name of Client, Enter last name first name of DSF user,
	if ErrorLevel
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") ;Tests to see if user input has last and first name
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
}
}
else if DROPDOWN = OSFNS
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of OSFNS user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else if DROPDOWN = OSFNS FIELD
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of OSFNS FIELD user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else if DROPDOWN = PUPIL TRANSPORT
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of OPT user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else if DROPDOWN = OPTDEV
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of OPTDEV user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else if DROPDOWN = PSAL
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of PSAL user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else
{
loop
{
InputBox, name, Enter School Code, Enter custodian school code,
if ErrorLevel
return
;RegExMatch(name, "i)[xkqmr]\d{3}", test)
;if test <> %name%
if !RegExMatch(name, "i)^[xkqmr]\d{3}$")
		{
		MsgBox,, Error, You did not enter the school code correctly (eg. x123).
		continue 
		}
	else
	break
	}
StringLower, name, name
}
}

I had made over 30 regular, as opposed to irregular, tickets using this model. So any change outside of the four functions, I was looking at 30 or more changes at least.

Now onto the OOP conversion, this is the class I used:

class Ticket
{
	__New(template, audible = 0)
	{
		this.template := template
		this.audible := audible
		
			global StartTime
			this.Userprompt(this.audible)
			StartTime := A_TickCount
			if ErrorLevel <> 0
			return
			else
			this.Workorder(this.audible)
			Send % this.template
			Sleep 200
			Send {ENTER}
			Sleep 200
			this.Division(this.audible)
			this.Timer(this.audible)
			;return
		
	}



Userprompt(audible = 0) ;Begins the first prompt for user's name
{
global
if (audible = "pos")
{
return
}
else
Sleep 200
WinWait, Track-It! Technician Client, ,3
if ErrorLevel
{
Run C:\Documents and Settings\%A_Username%\Start Menu\Programs\Numara Track-It!\Numara Track-It! Technician Client.appref-ms
;MsgBox, TrackIt! is not open.
return
}
else if DROPDOWN = DSF
{
loop
{
InputBox, name, Enter Name of Client, Enter last name first name of DSF user,
	if ErrorLevel
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") ;Tests to see if user input has last and first name
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
}
}
else if DROPDOWN = OSFNS
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of OSFNS user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else if DROPDOWN = OSFNS FIELD
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of OSFNS FIELD user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else if DROPDOWN = PUPIL TRANSPORT
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of OPT user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else if DROPDOWN = OPTDEV
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of OPTDEV user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else if DROPDOWN = PSAL
{
loop
	{
InputBox, name, Enter Name of Client, Enter last name first name of PSAL user,
if ErrorLevel 
	return
	if !RegExMatch(name, "^.{1,}\s.{1,}$") 
		{
		MsgBox,, Error, You did not enter the username correctly (eg. Surname Given name).
		continue 
		}
	else
	break
	}
}
else
{
loop
{
InputBox, name, Enter School Code, Enter custodian school code,
if ErrorLevel
return
;RegExMatch(name, "i)[xkqmr]\d{3}", test)
;if test <> %name%
if !RegExMatch(name, "i)^[xkqmr]\d{3}$")
		{
		MsgBox,, Error, You did not enter the school code correctly (eg. x123).
		continue 
		}
	else
	break
	}
StringLower, name, name
}
}

Workorder(audible = 0) ;Creates the work order and opens template selection
{
global
IfWinNotActive Track-It! Technician Client,,3  
WinActivate Track-It! Technician Client,,3
WinWaitActive Track-It! Technician Client,,3
if ErrorLevel
{
    MsgBox, WinWait timed out.
    return
}
else
Sleep, 200
Send, {CTRLDOWN}w{CTRLUP} ;Creates a new work order
WinWait, Work Order - New, ,,,Billing Information
;IfWinNotActive, Work Order - New, ,,,Look for the time prompt! 
WinActivate, Work Order - New, ,,,Billing Information
WinWaitActive, Work Order - New, ,,,Billing Information
Sleep 300
WinGet, workorder, ID, Work Order - New
WinWait ahk_id %workorder%,,10
IfWinNotActive ahk_id %workorder%,,10
WinActivate ahk_id %workorder%,,10
WinWaitActive ahk_id %workorder%,,10
if ErrorLevel
{
    MsgBox, WinWait timed out.
    return
}
else
Sleep, 200
if (audible != "pos")
{
Send, %name% {ENTER}
}
Sleep, 200
Send, {ALTDOWN}ams{ALTUP} ;Function to call up template selection
StartTime := A_TickCount
WinWait Apply Template,,3
IfWinNotActive Apply Template,,3
WinActivate Apply Template,,3
WinWaitActive Apply Template,,3
if ErrorLevel
{
    MsgBox, WinWait timed out.
    return
}
}

Division(audible = 0) ;Writes in the division and department of the user
{
global
if (audible = "pos")
{
return
}
else
if DROPDOWN = DSF
Send, {ALTDOWN}uu{ALTUP}d{ENTER}{ALTDOWN}m{ALTUP}d{ENTER} ;Selects DSFNT4 selection
else if DROPDOWN = OSFNS
Send, {ALTDOWN}uu{ALTUP}f{DOWN}{ENTER}{ALTDOWN}m{ALTUP}f{ENTER} ;Selects for OSFNS selection
else if DROPDOWN = CENTRAL
Send, {ALTDOWN}uu{ALTUP}c{ENTER}{ALTDOWN}m{ALTUP}c{ENTER} ;Selects for Central users selection
else if DROPDOWN = PUPIL TRANSPORT
Send, {ALTDOWN}uu{ALTUP}p{DOWN 2}{ENTER}{ALTDOWN}m{ALTUP}p{ENTER} ;Selects for PUPIL TRANSPORT selection
else if DROPDOWN = OPTDEV
Send, {ALTDOWN}uu{ALTUP}a{ENTER}{ALTDOWN}m{ALTUP}o{ENTER} ;Selects for OPTDEV settings
else if DROPDOWN = PSAL
Send, {ALTDOWN}uu{ALTUP}a{DOWN}{ENTER}{ALTDOWN}m{ALTUP}f{ENTER} ;Selects for PSAL selection
else
Send, {ALTDOWN}uu{ALTUP}f{ENTER}{ALTDOWN}m{ALTUP}f{ENTER} ;Selects for OSFNS Field users selection
Sleep 200
Send {ALTDOWN}ii{ALTUP}.{ENTER} ;To select NON-POS user
}

Timer(audible = 0)
{
global ;StartTime
if (audible = "pos")
{
return
}
else
WinWait, ahk_id %workorder%, 
IfWinNotActive, ahk_id %workorder%, , WinActivate, ahk_id %workorder%, 
WinWaitActive, ahk_id %workorder%, 
Send {ALTDOWN}vtoo{ALTUP}{ENTER}
Sleep 200
Send {ALTDOWN}ooo{ALTUP}
SendRaw Look for the time prompt!
MsgBox,, %LISTBOX%, Press OK once you have finished resolving the ticket.
;MsgBox %StartTime%
;IfMsgbox Ok
	;EndTime := A_Now
;MsgBox %EndTime%
ElapsedTime := A_TickCount - StartTime
;Msgbox %ElapsedTime%
SetFormat, Float, 0.2
ElapsedTime /= 3600000.00
MsgBox,, Elapsed Time, %ElapsedTime%
Sleep 200
WinWait ahk_id %workorder%,,10
IfWinNotActive ahk_id %workorder%,,10
WinActivate ahk_id %workorder%,,10
WinWaitActive ahk_id %workorder%,,10
if ErrorLevel
{
    MsgBox, WinWait timed out.
    return
}
else
;Sleep 200
;Send, {ALTDOWN}vtoo{ALTUP}{ENTER}
;Sleep 200
;Send {ALTDOWN}ooo{ALTUP}
;SendRaw Look for the time prompt!
Sleep 200
Send {ALTDOWN}oo{ALTUP}
Send %ElapsedTime%
return
}

Technician(tech = 0)
{
	if (tech = "pos")
		Send !g.pos{ENTER}
	else if (tech = "evens")
		Send !gjos{ENTER}
	else if (tech = "field")
		Send !g.fie{ENTER}
	else
		return
	return	
}

}

Basically, I put in all my older functions in a class format ready for object deployment. Now all my tickets looked like this:

DesktopUpdateandMaintenance()
{
desktopupdate := new Ticket("04")
}

Each new ticket effectively became an object from the class I made. Now if there were any changes to the ticket making process, I would just tinker with the class while leaving the 30 or so objects intact, yay!

What's even cooler is that OOP gave me the ability to control the ticketing software using methods in the class. So I had a feature where I can choose the "Technician" of the ticket, it looked like this:

POSComboSchoolRequest()
{
comborequest := new Ticket("001", "pos")
comborequest.Technician("pos")
}

When the new object/ticket is made, I can use a method of that very same object, called Technician in this case, to introduce a parameter to send the name of the technician. Very neat!

Well, this has been a very quick and dirty run-through on what I did with OOP. I was playing around with meta-functions until I gave up on the whole process because I did not feel confident nor knowledgeable about AHK_L objects and its future.

If you have any more questions, feel free to ask. I know this is not the clearest example, but it was the only one that I made, and that worked in the real world. Thanks to everyone in the AHK_L community!

#2 comvox

comvox
  • Members
  • 137 posts

Posted 11 April 2012 - 04:42 AM

As one of the people who inquired about your OOP experience, I'd like to thank you for posting this example.
I'm glad that OOP has helped you simplify your work. I have to admit that I am still trying to work through your example, but it's useful to try to see how you think through the scripting problem. In my own experience, there are alternate ways to accomplish the same scripting problem (varying according to both the scripter and the scripting problem), and I'm trying to figure out for what type of problems the different methods shine.

#3 plastikglass

plastikglass
  • Members
  • 93 posts

Posted 11 April 2012 - 05:05 AM

No problem :wink:

What I really like about objects and classes is that you truly control whole software with just one class :O

What I mean by that is that the ticket software I was using was being manipulated with functions originally. But with objects, I made the entire ticketing software a class. In that class, I can include methods to do whatever I wanted to do with it: if I wanted to choose the technician's name, write a resolution, enter the duration of the ticket, choose the division, the user, the template, etc.

It would all be contained in that class. If I stuck with it, and my job, I could have created an entire OOP framework for our ticketing software that any competent programmer could manipulate.

For example:

Once I create the ticket.

Object := new Class(parameter)

I can go ahead and start filling in that ticket with these theoretical examples:

object.NameofTechnician(parameter)
object.NameofTemplate(parameter)
object.Duration(parameter)

And so on.

This was the beauty of objects for me. From one single class, I can create multiple tickets and do whatever I wanted with them in terms of customizing the ticket. And upon for first glance, you or anyone else can start using that syntax and make new tickets without even knowing what is inside the class! I wanted to create a user-friendly framework which would have achieved just that when I was at that job.

I thought this would be the best approach, which was the other methodology in which we could have the achieved same results? I am pretty interested in that also.

#4 Morpheus

Morpheus
  • Members
  • 396 posts

Posted 14 April 2012 - 09:58 PM

I was also one of the people who asked for this, so I want to say that I do appreciate that you took the time to post. I have to admit that I don't get the need for OOP in your example. It's likely that the reason that I don't get it is from a total lack of understanding of OOP itself. Occasionally I have to be beaten over the head to understand the benefits of a new concept.

Would you mind posting a more simplistic example of a script that would benefit from OOP?

I'm not being sarcastic, or argumentative. I would like to learn what OOP is all about, and why I need it.

#5 plastikglass

plastikglass
  • Members
  • 93 posts

Posted 16 April 2012 - 01:41 PM

I was also one of the people who asked for this, so I want to say that I do appreciate that you took the time to post. I have to admit that I don't get the need for OOP in your example. It's likely that the reason that I don't get it is from a total lack of understanding of OOP itself. Occasionally I have to be beaten over the head to understand the benefits of a new concept.

Would you mind posting a more simplistic example of a script that would benefit from OOP?

I'm not being sarcastic, or argumentative. I would like to learn what OOP is all about, and why I need it.


Yeah, I simply created this thread because I mentioned that I used objects for my project with AHK_L, and a few people wanted to see how I did it. The code itself is highly amateurish and proprietary, but it is real-world code that worked in a professional environment to get the job that I needed done.

I would love to write examples and show people the benefits, but I do not feel qualified to do that at all. But I have been discussing this subject in another thread, which I recommend you to join as well, where we discuss how to make AHK_L objects and its learning easier to accomplish. There are also several examples therein where they may help you out a great deal.

<!-- m -->http://autohotkey.co... ... 46#p528646<!-- m -->

I would also recommend the Head First book for C# and go through the first lab. They explain OOP in a much better manner, and when you come back to AHK objects, you will feel more confident and knowledgeable about how OOP actually works.