Re: Beginners OOP with AHK
Posted: 17 Dec 2018, 15:44
As I said earlier, as a tutorial homework, I added some methods to the FileSystem class in order to store the files and directory objects in an "Items" container inside the "FileContainer" sub class.
I defined the "__New()" method for the "FileContainer" sub class with two parameters: 1) the path of the drive (like C:\) or folder to add and 2) the number of levels of sub folders to scan. The "__New()" method is calling a new custom method named "getFilesInFolder()" that will collect the files and folders under the root path.
The custom method named "getFilesInFolder()" receives the same two parameters: 1) "thisPath" is the path of folder to scan and 2) "recurseLevels" is the number of levels of sub folders to scan. For each item found in "thisPath", the method "addItem()" is called to add the object returned by the "new FileSystem.Directory()" or "new FileSystem.File()" call.
In addition, for sub folders, the method "getFilesInFolder()" is called recursively for the sub folder with the "recurseLevels" decremented to keep track the number of levels scanned. The recursion continues as long as "recurseLevels" is positive. It stops when it gets down to zero. And if "recurseLevels" starts -1 (and lower with recursive calls), scan continues until the end of the directory branch.
The method "addItems()" is called to add the new "File" or "Directory" object to the "Items" array created in the "FileContainer" class. New items are added at the end or the array with a counter starting at 1.
The last new custom method "listFiles()" is called recursively to gather the list of items (folders and files) in the FileContainer. By default "listFiles()" returns all items in the container but parameters allow to pass a filter and select the number of recursion levels. Example are added to the full source below.
These changes can be tested with the following code. After having created your own instance of the "FileSystem" class, create a new instance of the "Directory" sub class for folders or of the "Drive" sub class for drives with the appropriate path and levels parameters. In both case, the "__New()" method of the parent class "FileContainer" is called with these parameters. There are various examples of parameters in the full sources. Then, the "listFiles()" method is called to gather the content of the "Items" array. This data is showed in a simple Gui.
I added a global variable "showTooltip" to control if the "getFilesInFolder()" displays a tooltip or not. There would probably be a better technique to pass the boolean value to the method but I could not figure how at this time. So I used this global variable as fall back.
I will post the full source of the modified class in the next message.
I defined the "__New()" method for the "FileContainer" sub class with two parameters: 1) the path of the drive (like C:\) or folder to add and 2) the number of levels of sub folders to scan. The "__New()" method is calling a new custom method named "getFilesInFolder()" that will collect the files and folders under the root path.
Code: Select all
class FileContainer extends FileSystem.FileSystemElement {
__New( path, recurseLevels := 1 ) {
; path -> folder or drive root to scan
; recurseLevels -> number of folder levels to scan (default 1 this path only, -1 to scan all, 0 to scan none)
if ( !fileExist( path ) )
Throw exception( "Path """ . path . """ doesn't exist", "__New", "Exist test returned false" )
if ( !inStr( fileExist( path ), "D" ) ) ; if file is not a folder or a drive
Throw exception( "Error creating File", "__New", "Path does not points to Folder" )
this.name := path
if ( SubStr( path, 0, 1 ) = "\" ) ; remove ending backslash for drive roots (like "C:\")
path := SubStr( path, 1, StrLen( path ) - 1 )
if !( this.getFilesInFolder( path, recurseLevels ) )
Throw exception( "Error getting Items", "__New", "Could not get items from container" )
}
In addition, for sub folders, the method "getFilesInFolder()" is called recursively for the sub folder with the "recurseLevels" decremented to keep track the number of levels scanned. The recursion continues as long as "recurseLevels" is positive. It stops when it gets down to zero. And if "recurseLevels" starts -1 (and lower with recursive calls), scan continues until the end of the directory branch.
Code: Select all
getFilesInFolder( thisPath, recurseLevels ) {
; thisPath -> folder or drive root to scan
; recurseLevels -> number of folder levels to scan including this one
if ( showToolTip )
ToolTip, Getting files from:`n%thisPath%
; if recurseLevels > 0 continue with sub folder
; if recurseLevels < 0 continue until the end of branch
; if recurseLevels = 0 stop recursion
if ( recurseLevels = 0 )
return true
this.Items := Object() ; create an object to contain items (files and folders)
Loop, Files, % thisPath . "\*.*", FD ; do not use "R" here, the class does the recursion below
{
if A_LoopFileAttrib contains H,S ; skip hidden or system files
continue
if A_LoopFileAttrib contains D ; this is a folder, create Directory object and recurse to sub level
objItem := new FileSystem.Directory( A_LoopFileFullPath, recurseLevels - 1 ) ; "- 1" to track the number of levels
else ; this is a file, create File object
objItem := new FileSystem.File( A_LoopFileFullPath )
this.addItem(objItem) ; add Directory or File object to Items container
}
return true
}
Code: Select all
addItem(objItem) {
this.Items.InsertAt(this.Items.Length()+ 1, objItem) ; add Directory or File object to Items container
}
Code: Select all
listFiles( filter := "", recurseLevels := -1 ) {
; filter -> exclude items with filter in their name, default empty (include all items)
; recurseLevels -> number of folder levels to scan (default -1 to scan all, 0 to scan none)
; if recurseLevels > 0 continue with sub folder
; if recurseLevels < 0 continue until the end of branch
; if recurseLevels = 0 stop recursion
if (recurseLevels = 0)
return
thisList := ""
for intKey, objItem in this.Items {
if !StrLen(filter) or InStr(objItem.name, filter)
thisList .= objItem.name . "`n"
if ( objItem.HasKey( "Items" ) ) ; this is a container, recurse
thisList .= objItem.listFiles( filter, recurseLevels - 1 ) ; "- 1" to track the number of levels
}
return thisList
}
Code: Select all
global showTooltip := True
MyFileSystem := new FileSystem ; create my instance of the class
MyNewContainer := new MyFileSystem.Directory( ) ; create a container for the specified folder and get files in this folder only
if ( showTooltip )
ToolTip ; hide last tooltip
; get content from the MyNewContainer object
str := MyNewContainer.listFiles() ; get the list of files and folders contained in the MyNewContainer object
; show content in Gui
Gui, Add, Edit, w800 r25 ReadOnly, % SubStr( str, 1, 30000 ) . ( StrLen( str ) > 30000 ? "`n..." : "" ) ; limit because of 32k limit of Edit control
Gui, Add, Button, default, Close
GuiControl, Focus, Close
Gui, Show
return
ButtonClose:
ExitApp
I will post the full source of the modified class in the next message.