PureBasic Docs- Ideas/Help needed for a "We start" chapter!?

Everything else that doesn't fall into one of the other PB categories.
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2056
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

blueznl wrote:Feel free to rip parts of the guide, I've said this ages ago, so I'll just repeat it once more... A little note would be nice but not even required.
Thanks blueznl.
We will see, what can be used (it still should result in a relative short introduction, which is linking to further documentation in the PB manual).

Notes in the manual for all people, which are contributing a lot of help, will be done for sure! :-)
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Storing Data in Memory item - updated

Post by spikey »

This example gathers information about the files in the logged on user's home directory into a structured linked list. For now the output isn't very exciting but we will come back to this example later on and make it a bit more friendly in several different ways.

Code: Select all

; This section describes the fields of a structure or record, mostly integers in this case,
; but notice the string for the file name and the quad for the file size.
Structure FILEITEM
  Name.S
  Attributes.I
  Size.Q
  DateCreated.I
  DateAccessed.I
  DateModified.I
EndStructure

; Now we define a new list of files using the structure previously specified
; and some other working variables we'll use later on.
NewList FilesList.FILEITEM()
Define Folder$
Define.I Result

; This function gets the home directory for the logged on user.
Folder$ = GetHomeDirectory()

; Open the directory to enumerate all its contents.
Result = ExamineDirectory(0, Folder$, "*.*") 

; If this is ok, begin enumeration of entries.
If Result
 
  ; Loop through until NextDirectoryEntry(0) becomes zero - indicating that there are no more entries.
  While NextDirectoryEntry(0)
   
    ; If the directory entry is a file, not a folder.
    If DirectoryEntryType(0) = #PB_DirectoryEntry_File
     
      ; Add a new element to the linked list.
      AddElement(FilesList())
     
      ; And populate it with the properties of the file.
      FilesList()\Name = DirectoryEntryName(0)
      FilesList()\Size = DirectoryEntrySize(0)
      FilesList()\Attributes = DirectoryEntryAttributes(0)
      FilesList()\DateCreated = DirectoryEntryDate(0, #PB_Date_Created)
      FilesList()\DateAccessed = DirectoryEntryDate(0, #PB_Date_Accessed)
      FilesList()\DateModified = DirectoryEntryDate(0, #PB_Date_Modified)

    EndIf
   
  Wend
 
  ; Close the directory.
  FinishDirectory(0)
 
EndIf

; If there are some entries in the list, show the results in the debug window.
If ListSize(FilesList())
 
  ForEach FilesList()
   
    Debug "Filename = " + FilesList()\Name
    Debug "Size = " + Str(FilesList()\Size)
    Debug "Attributes = " + StrU(FilesList()\Attributes)
    Debug "Created = " + StrU(FilesList()\DateCreated)
    Debug "Accessed = " + StrU(FilesList()\DateAccessed)
    Debug "Modified = " + StrU(FilesList()\DateModified)
   
  Next FilesList()
 
EndIf
Ok, firstly, the dates in the output are just numbers - this isn't very helpful, so let's make them look a bit more familar. Replace the last three Debug statements with these:

Code: Select all

...
Debug "Created = " + FormatDate("%dd/%mm/%yyyy", FilesList()\DateCreated)
Debug "Accessed = " + FormatDate("%dd/%mm/%yyyy", FilesList()\DateAccessed)
Debug "Modified = " + FormatDate("%dd/%mm/%yyyy", FilesList()\DateModified)
The FormatDate function takes a date in PureBasic's own numeric date format and displays it in a format that we can specify. So now things should begin to look a bit more sensible.

Finally, for now, the list isn't in any particular order, so let's sort the list before we display it. Add this line before the comment about showing the list and the ForEach loop.

Code: Select all

...

; Sort the list into ascending alphabetical order of file name.
SortStructuredList(FilesList(), #PB_Sort_Ascending, OffsetOf(FILEITEM\Name), #PB_Sort_String)

; If there are some entries in the list, show the results in the debug window.

...
This command takes the structured list, and resorts it into ascending order (#PB_Sort_Ascending), of the Name field of the structure (OffsetOf(FILEITEM\Name)), which is a string value (#PB_Sort_String).
Last edited by spikey on Wed Aug 03, 2011 6:01 pm, edited 1 time in total.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by spikey »

In the previous item "Input & Output" you already saw an overview about the different possibilities to output text to the user, and in the item "Storing Data in Memory", we started to build a small application to display the properties of files in a particular folder to the debug window.

Now we're going to revisit this example to improve the data output section to resolve some issues with using the debug window. Firstly, this window is only available in the PureBasic IDE which means its only useful to programmers, secondly it doesn't really give us much control over how our output looks.

PureBasic provides a text mode window, or console window, which can be used in compiled programs. So let's update our example to use it instead.

First, we will need some extra working variables to make this work properly. Amend the variable definitions like this:-

Code: Select all

...

; Now we define a list of files using the structure previously specified.
NewList FilesList.FILEITEM()
Define Access$, Attrib$, Create$, Folder$, Modify$, Message$, Num$, Size$
Define.I Result

...
Next, remove the output section of code completely, from the comment line:-

Code: Select all

; If there are some entries in the list, show the results in the debug window.
...
Now replace this with:-

Code: Select all


; Open a text mode screen to show the results.
OpenConsole()

; Display a title.
; PrintN displays the string given in the console window and moves the print position to the start of the next line afterwards.
; Space(n) returns n spaces in a string.
PrintN("File list of " + Folder$ + ".")
PrintN("-------------------------------------------------------------------------------")
Message$ = "Num Name"
PrintN(Message$)
Message$ = Space(4) + "Create" + Space(5) + "Access" + Space(5) + "Modify" + Space(5) + "Attrib Size"
PrintN(Message$)

; If there are some entries in the list, show the results in the console window.
If ListSize(FilesList())
 
  ; Loop through the linked list to display the results.
  ForEach FilesList()
   
    ; Tabulate the number of the list index.
    ; ListIndex() returns the current position in the list, counting from zero.
    ; StrU converts an unsigned number into a string.
    ; RSet pads a string to a set length with the necessary number of a specified character at the front.
    ; Here we use it to make sure all the index numbers are padded to 3 characters in length.
    Num$ = RSet(StrU(ListIndex(FilesList()) + 1), 3, " ")
   
    ; Display the item number and file name.
    Message$ = Num$ + " " + FilesList()\Name
    PrintN(Message$)
   
    ; These lines convert the three date values to something more familiar.
    Create$ = FormatDate("%dd/%mm/%yyyy", FilesList()\DateCreated)
    Access$ = FormatDate("%dd/%mm/%yyyy", FilesList()\DateAccessed)
    Modify$ = FormatDate("%dd/%mm/%yyyy", FilesList()\DateModified)
   
    ; Convert the file size to a padded string the same as with the index value above,
    ; but allow space for the maximum size of a quad.
    Size$ = RSet(StrU(FilesList()\Size), 19)
   
    ; Convert the attributes to a string, for now.
    Attrib$ = RSet(StrU(FilesList()\Attributes), 6, " ")
   
    ; Display the file's properties.
    Message$= Space(4) + Create$ + " " + Access$ + " " + Modify$ + " " + Attrib$ + " " + Size$
    PrintN(Message$)
   
    ; Display a blank line.
    PrintN("")
   
  Next FilesList()
 
EndIf

; Wait for the return key to be displayed, so the results can be viewed before the screen closes.
PrintN("")
PrintN("Press return to exit")
Input()
All being well the output should appear in a console window looking something like this:-

Code: Select all

File list of C:\Documents and Settings\user\.
-------------------------------------------------------------------------------
Num Name
    Create     Access     Modify     Attrib Size
  1 NTUSER.DAT
    03/07/2008 04/04/2011 02/04/2011     34            18874368

  2 kunzip.dll
    14/07/2008 04/04/2011 14/07/2008     32               18432

  3 ntuser.dat.LOG
    03/07/2008 04/04/2011 04/04/2011     34                1024

  4 ntuser.ini
    03/07/2008 02/04/2011 02/04/2011      6                 278

Press return to exit
This output is from a Windows XP system, later versions of Windows and Linux or Mac OSX will show different files of course.
Last edited by spikey on Wed Aug 03, 2011 6:11 pm, edited 3 times in total.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Graphical User Interface item

Post by spikey »

In addition to the console window, PureBasic supports the creation of graphical user interfaces (GUI) too. So let's revisit the file properties example from previous items again and turn it into a GUI application.
Note that PureBasic provides a far easier way of getting this particular job done already - the ExplorerListGadget; but, as the example is intended to introduce managing GUI elements, using that gadget would defeat this object a bit.

Code: Select all

; The structure for file information as before.
Structure FILEITEM
  Name.S
  Attributes.I
  Size.Q
  DateCreated.I
  DateAccessed.I
  DateModified.I
EndStructure

; This is a constant to identify the window.
Enumeration
  #wdwFiles
EndEnumeration

; This is an enumeration to identify controls which will appear on the window.
Enumeration
  #txtFolder
  #lsiFiles
EndEnumeration

; Now we define a list of files using the structure previously specified.
NewList lstFiles.FILEITEM()

; And some working variables to make things happen.
Define.S strAccess, strAttrib, strCreate, strFolder, strModify, strMsg, strNum, strSize
Define.L lngResult, lngFlags

; These variables will receive details of GUI events as they occur in the program.
Define.L Event, EventWindow, EventGadget, EventType, EventMenu

; This function gets the home directory for the logged on user.
strFolder = GetHomeDirectory()

; Open the directory to enumerate its contents.
lngResult = ExamineDirectory(0, strFolder, "*.*")  

; If this is ok, begin enumeration of entries.
If lngResult
  
  ; Loop through until NextDirectoryEntry(0) becomes zero - indicating that there are no more entries.
  While NextDirectoryEntry(0)
    
    ; If the directory entry is a file, not a folder.
    If DirectoryEntryType(0) = #PB_DirectoryEntry_File
      
      ; Add a new element to the linked list.
      AddElement(lstFiles())
      
      ; And populate it with the properties of the file.
      lstFiles()\Name = DirectoryEntryName(0)
      lstFiles()\Size = DirectoryEntrySize(0)
      lstFiles()\Attributes = DirectoryEntryAttributes(0)
      lstFiles()\DateCreated = DirectoryEntryDate(0, #PB_Date_Created)
      lstFiles()\DateAccessed = DirectoryEntryDate(0, #PB_Date_Accessed)
      lstFiles()\DateModified = DirectoryEntryDate(0, #PB_Date_Modified)

    EndIf
    
  Wend
  
  ; Close the directory.
  FinishDirectory(0)
  
EndIf

; Sort the list into ascending alphabetical order of file name.
SortStructuredList(lstFiles(), #PB_Sort_Ascending, OffsetOf(FILEITEM\Name), #PB_Sort_String)

; The interesting stuff starts to happen here...

; This line defines a flag for the window attributes by OR-ing together the desired attribute constants.
lngFlags = #PB_Window_SystemMenu |#PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_TitleBar

; Open a GUI window.
Openwindow(#wdwFiles, 50, 50, 400, 400, "File Properties", lngFlags)
; A text gadget to show the name of the folder.
TextGadget(#txtFolder, 5, 40, 390, 25, strFolder)
; A list icon gadget to hold the file list and properties.
ListIconGadget(#lsiFiles, 5, 70, 390, 326, "#", 30)
; Add columns to the ListIconGadget to hold each property.
AddGadgetColumn(#lsiFiles, 1, "Name", 200)
AddGadgetColumn(#lsiFiles, 2, "Created", 90)
AddGadgetColumn(#lsiFiles, 3, "Accessed", 90)
AddGadgetColumn(#lsiFiles, 4, "Modified", 90)
AddGadgetColumn(#lsiFiles, 5, "Attributes", 150)
AddGadgetColumn(#lsiFiles, 6, "Size", 150)

; Load the files into the list view.
ForEach lstFiles()

  ; Display the item number and file name.
  strNum = StrU(ListIndex(lstFiles()) + 1)
  
  ; These lines convert the three date values to something more familiar.
  strCreate = FormatDate("%dd/%mm/%yyyy", lstFiles()\DateCreated)
  strAccess = FormatDate("%dd/%mm/%yyyy", lstFiles()\DateAccessed)
  strModify = FormatDate("%dd/%mm/%yyyy", lstFiles()\DateModified)
  
  ; Convert the file size to a padded string the same as with the index value above,
  ; but allow space for the maximum size of a quad.
  strSize = StrU(lstFiles()\Size)
  
  ; Convert the attributes to a string, for now.
  strAttrib = StrU(lstFiles()\Attributes)
  
  ; Build a row string.  
  ; The Line Feed character 'Chr(10)' tells the gadget to move to the next column.
  strMsg = strNum + Chr(10) + lstFiles()\Name + Chr(10) + strCreate + Chr(10) + strAccess + Chr(10) + strModify + Chr(10) + strAttrib + Chr(10) + strSize
  
  ; Add the row to the list view gadget.
  AddGadgetItem(#lsiFiles, -1, strMsg)
  
Next lstFiles()

; This is the event loop for the window.  
; It will deal with all the user interaction events that we wish to use. 

Repeat
  ; Wait until a new window or gadget event occurs.
  Event = WaitwindowEvent()
  ; In programs with more than one form, which window did the event occur on.
  EventWindow = EventWindow()
  ; Which gadget did the event occur on.
  EventGadget = EventGadget()
  ; What sort of event occurred.
  EventType = EventType()
  
  ; Take some action.
  Select Event
      
    Case #PB_Event_Gadget
      ; A gadget event occurred.
      If EventGadget = #txtFolder
      ElseIf EventGadget = #lsiFiles
      EndIf
      
    Case #PB_Event_Closewindow
      ; The window was closed.
      If EventWindow = #wdwFiles
        Closewindow(#wdwFiles)
        Break
      EndIf
      
  EndSelect
  
  ; Go round and do it again.
  ; In practice the loop isn't infinite because it can be stopped by clicking the window's Close button.
ForEver
At this point the application already has some useful features. However, it has some problems too:-
1) You can't choose a folder to show.
2) You can't update the list contents without closing and restarting the program.
3) If you resize the window, the gadgets don't resize with it.
4) The attributes column is still not very useful.
We will revisit this program again later on to fix all these issues.
Last edited by spikey on Fri Apr 08, 2011 9:13 pm, edited 2 times in total.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Constants item

Post by spikey »

In addition to variables PureBasic provides a method to define constants too. In fact it provides several. We’ll have a quick look at them now.

Predefined constants - provided either by PureBasic itself, these all begin #PB_, or from the API for the operating system. The IDE’s "Structure Viewer" tool has a panel which shows all the predefined constants.

User defined constants - by defining a constant name with the prefix # you can provide your own constants to make code more readable.

Code: Select all

#MyConstant1 = 10
#MyConstant2 = “Hello, World!”
Enumerations – PureBasic will automatically number a series of constants sequentially in an Enumeration, by default enumerations will begin from zero – but this can be altered, if desired.

Code: Select all

Enumeration
  #MyConstantA
  #MyConstantB
  #MyConstantC
EndEnumeration

Enumeration 10 Step 5
  #MyConstantD ; will be 10
  #MyConstantE ; will be 15
  #MyConstantF ; will be 20
EndEnumeration
Last edited by spikey on Wed Apr 13, 2011 1:40 pm, edited 1 time in total.
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2056
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

I've added further contributions by spikey to the first post.

I've sorted them into the existing topic structure, also 2 new topics (Constants & Building a GUI) I've added.


@spikey: Feel free to tell me, if anything is placed at the false position! Thank you very much! :D
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
bembulak
Enthusiast
Enthusiast
Posts: 572
Joined: Mon Mar 06, 2006 3:53 pm
Location: Austria

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by bembulak »

@Andre:
Feel free to copy parts from here, if you want (Tutorial is in German language). It was aimed at absolute beginners, is a little longer than just one page, but it tries build upon the previous chapter.
http://purebook.purebasic-lounge.com/in ... ic.Vorwort
It's been edited and altered over the past years, but I guess I still have the original version as ODF on my disk.

Maybe we need a little "Head First - Programming PureBasic"?
cheers,

bembulak
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Displaying graphics output & simple drawing item

Post by spikey »

This example show how to create a simple drawing. It uses the 2D drawing commands to draw two sine waves at different frequencies and shows the harmonic produced by combining the two waves. It uses procedures, which we will discuss in more detail later on, to break the drawing tasks into three self-contained tasks.
Drawing the axes - demonstrates the Line command.
Drawing the legend - demonstrates the Box and DrawText commands.
Drawing the wave forms - demonstrates the LineXY command and shows how to use color.

Code: Select all

; Form.
Enumeration
  #wdwHarmonic
EndEnumeration

; Gadgets.
Enumeration
  #txtPlot1
  #cboPlot1
  #txtPlot2
  #cboPlot2
  #imgPlot
EndEnumeration

; Image.
Enumeration
  #drgPlot
EndEnumeration

; Event variables.
Define.L Event, EventWindow, EventGadget, EventType, EventMenu

; Implementation.
Procedure CreateWindow()
  ; Creates the window and gadgets.  

  If OpenWindow(#wdwHarmonic, 450, 200, 650, 680, "Harmonics", #PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_MinimizeGadget|#PB_Window_TitleBar)
    
    ; This is a non-visual gadget used to draw the image, later its contents will be displayed in #imgPlot.
    CreateImage(#drgPlot, 640, 640, 24)
    
    ; Label for the Plot 1 combo.
    TextGadget(#txtPlot1, 2, 5, 40, 20, "Plot 1:")
    
    ; The Plot 1 combo.
    ComboBoxGadget(#cboPlot1, 45, 5, 150, 20)
    AddGadgetItem(#cboPlot1, 0, "Sin(X)")
    AddGadgetItem(#cboPlot1, 1, "Sin(X * 2)")
    AddGadgetItem(#cboPlot1, 2, "Sin(X * 3)")
    AddGadgetItem(#cboPlot1, 3, "Sin(X * 4)")
    AddGadgetItem(#cboPlot1, 4, "Sin(X * 5)")
    AddGadgetItem(#cboPlot1, 5, "Sin(X * 6)")
    
    ; Select Sin(X)
    SetGadgetState(#cboPlot1, 0)
    
    ; Label for the Plot 1 combo.
    TextGadget(#txtPlot2, 210, 5, 40, 20, "Plot 2:")
    
    ; The Plot 1 combo.
    ComboBoxGadget(#cboPlot2, 255, 5, 150, 20)
    AddGadgetItem(#cboPlot2, 0, "Sin(X)")
    AddGadgetItem(#cboPlot2, 1, "Sin(X * 2)")
    AddGadgetItem(#cboPlot2, 2, "Sin(X * 3)")
    AddGadgetItem(#cboPlot2, 3, "Sin(X * 4)")
    AddGadgetItem(#cboPlot2, 4, "Sin(X * 5)")
    AddGadgetItem(#cboPlot2, 5, "Sin(X * 6)")
    
    ; Select Sin(X * 2), otherwise the initial display is a bit uninteresting.
    SetGadgetState(#cboPlot2, 1)
    
    ; The visual image gadget on the window.
    ImageGadget(#imgPlot, 2, 30, 646, 616, 0, #PB_Image_Border)
    
  EndIf
  
EndProcedure

Procedure PlotAxes()
  ; Draws the axes on the image #drgPlot.

  ; Send drawing commands to #drgPlot.
  StartDrawing(ImageOutput(#drgPlot))
  
  ; Draw a white background.
  Box(0, 0, ImageWidth(#drgPlot), ImageHeight(#drgPlot), #White)
  
  ; Draw the axes in black.
  Line(1, 1, 1, ImageHeight(#drgPlot) - 2, #Black)
  Line(1, (ImageHeight(#drgPlot) - 2) /2, ImageWidth(#drgPlot) -2, 1, #Black)
  
  ; Finished drawing.
  StopDrawing()
  
EndProcedure

Procedure PlotLegend(alngPlot1, alngPlot2)
  ; Draws the legend on the image #drgPlot.
  
  Protected.S strFunc1, strFunc2, strLabel1, strLabel2, strLabel3
  
  ; Set label text 1.
  If alngPlot1 = 0 
    strFunc1 = "Sin(X)"
    
  Else
    strFunc1 = "Sin(X * " + StrU(alngPlot1 + 1) + ")"
    
  EndIf
  
  ; Set label text 2.
  If alngPlot2 = 0 
    strFunc2 = "Sin(X)"
    
  Else
    strFunc2 = "Sin(X * " + StrU(alngPlot2 + 1) + ")"
    
  EndIf
  
  ; Set label text.
  strLabel1 = "Y = " + strFunc1
  strLabel2 = "Y = " + strFunc2
  strLabel3 = "Y = " + strFunc1 + " + " + strFunc2 
  
  ; Draw legend.
  StartDrawing(ImageOutput(#drgPlot))
  
  ; Box.
  DrawingMode(#PB_2DDrawing_Outlined)
  Box(20, 10, TextWidth(strLabel3) + 85, 80, #Black)
  
  ; Label 1.
  Line(30, 30, 50, 1, #Blue)
  DrawText(95, 22, strLabel1, #Black, #White)
  
  ; Label 2.
  Line(30, 50, 50, 1, #Green)
  DrawText(95, 42, strLabel2, #Black, #White)
  
  ; Label 3.
  Line(30, 70, 50, 1, #Red)
  DrawText(95, 62, strLabel3, #Black, #White)
  
  StopDrawing()
  
EndProcedure

Procedure PlotFunction(alngPlot1, alngPlot2)
  ; Draws the waveforms on the image #drgPlot.

  Protected.L lngSX, lngEX
  Protected.F fltRad1, fltRad2, fltSY1, fltEY1, fltSY2, fltEY2, fltSY3, fltEY3
  
  StartDrawing(ImageOutput(#drgPlot))
  
  ; Set initial start points for each wave.
  lngSX = 1
  fltSY1 = ImageHeight(#drgPlot) / 2
  fltSY2 = fltSY1
  fltSY3 = fltSY1
  
  ; Plot wave forms.
  For lngEX = 1 To 720
    
    ; Sine function works in radians, so convert from degrees and calculate sine.
    
    ; Function 1
    If alngPlot1 = 0 
      fltRad1 = Sin(Radian(lngEX))
      
    Else
      ; If the function should have a multiplier, account for this.
      fltRad1 = Sin(Radian(lngEX) * (alngPlot1 + 1))
      
    EndIf
    
    ; Function 2
    If alngPlot2 = 0 
      fltRad2 = Sin(Radian(lngEX))
      
    Else
      fltRad2 = Sin(Radian(lngEX) * (alngPlot2 + 1))
      
    EndIf
    
    ; Plot function 1 in blue.
    ; Calculate end Y point.
    fltEY1 = (ImageHeight(#drgPlot) / 2) + (fltRad1 * 100)
    ; Draw a line from the start point to the end point.
    LineXY(lngSX, fltSY1, lngEX, fltEY1, #Blue)
    ; Update the next start Y point to be the current end Y point.
    fltSY1 = fltEY1
    
    ; Plot function 2 in green.
    fltEY2 = (ImageHeight(#drgPlot) / 2) + (fltRad2 * 100)
    LineXY(lngSX, fltSY2, lngEX, fltEY2, #Green)
    fltSY2 = fltEY2
    
    ; Plot harmonic in red.
    fltEY3 = (ImageHeight(#drgPlot) / 2) + ((fltRad1 + fltRad2) * 100)
    LineXY(lngSX, fltSY3, lngEX, fltEY3, #Red)
    fltSY3 = fltEY3
    
    ; Update the start X point to be the current end X point.
    lngSX = lngEX
    
  Next lngEX
  
  StopDrawing()
  
EndProcedure

;- Main
CreateWindow()
PlotAxes()
PlotLegend(GetGadgetState(#cboPlot1), GetGadgetState(#cboPlot2))
PlotFunction(GetGadgetState(#cboPlot1), GetGadgetState(#cboPlot2))

; Reload the image gadget now drawing is complete.
ImageGadget(#imgPlot, 2, 30, 646, 616, ImageID(#drgPlot), #PB_Image_Border)

;- Event loop
Repeat
  
  Event = WaitWindowEvent()
  EventWindow = EventWindow()
  EventGadget = EventGadget()
  EventType = EventType()
  
  Select Event
      
    Case #PB_Event_Gadget
      If EventGadget = #txtPlot1 Or EventGadget = #txtPlot2
        ; Do nothing.
        
      ElseIf EventGadget = #imgPlot
        ; Do nothing.
        
      ElseIf EventGadget = #cboPlot1 Or EventGadget = #cboPlot2
        ; If one of the combo boxes changed, redraw the image.
        PlotAxes()
        PlotLegend(GetGadgetState(#cboPlot1), GetGadgetState(#cboPlot2))
        PlotFunction(GetGadgetState(#cboPlot1), GetGadgetState(#cboPlot2))
        ImageGadget(#imgPlot, 2, 30, 646, 616, ImageID(#drgPlot), #PB_Image_Border)
        
      EndIf
      
    Case #PB_Event_CloseWindow
      If EventWindow = #wdwHarmonic
        CloseWindow(#wdwHarmonic)
        Break
      EndIf
      
  EndSelect
  
ForEver
Last edited by spikey on Wed Apr 13, 2011 7:01 pm, edited 2 times in total.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by spikey »

@Andre:
That location is fine - while writing the GUI item it occurred to me that I shouldn't start using Enumerations without introducing them properly first.

However I see that I made a couple of mistakes in that example, it won't compile as it stands! :oops: I've corrected my post now.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Procedures item

Post by spikey »

We're going to revisit the file properties example again. This time to introduce procedures and to address some of the limitations identified in the program in previous items.

Code: Select all

; The structure for file information as before.
Structure FILEITEM
  Name.S
  Attributes.I
  Size.Q
  DateCreated.I
  DateAccessed.I
  DateModified.I
EndStructure

; This is a constant to identify the window.
Enumeration
  #wdwFiles
EndEnumeration

; This is an enumeration to identify controls that will appear on the window.
Enumeration
  #btnFolder
  #btnUpdate
  #txtFolder
  #lsiFiles
EndEnumeration

Procedure FilesExamine(astrFolder.S, List alstFiles.FILEITEM())
  ; Obtains file properties from strFolder into lstFiles.
  
  Protected.L lngResult
  
  ; Clear current contents.
  ClearList(alstFiles())
  
  ; Open the directory to enumerate its contents.
  lngResult = ExamineDirectory(0, astrFolder, "*.*")  
  
  ; If this is ok, begin enumeration of entries.
  If lngResult
    
    ; Loop through until NextDirectoryEntry(0) becomes zero - indicating that there are no more entries.
    While NextDirectoryEntry(0)
      
      ; If the directory entry is a file, not a folder.
      If DirectoryEntryType(0) = #PB_DirectoryEntry_File
        
        ; Add a new element to the linked list.
        AddElement(alstFiles())
        
        ; And populate it with the properties of the file.
        alstFiles()\Name = DirectoryEntryName(0)
        alstFiles()\Size = DirectoryEntrySize(0)
        alstFiles()\Attributes = DirectoryEntryAttributes(0)
        alstFiles()\DateCreated = DirectoryEntryDate(0, #PB_Date_Created)
        alstFiles()\DateAccessed = DirectoryEntryDate(0, #PB_Date_Accessed)
        alstFiles()\DateModified = DirectoryEntryDate(0, #PB_Date_Modified)
        
      EndIf
      
    Wend
    
    ; Close the directory.
    FinishDirectory(0)
    
  EndIf
  
  ; Sort the list into ascending alphabetical order of file name.
  SortStructuredList(alstFiles(), #PB_Sort_Ascending, OffsetOf(FILEITEM\Name), #PB_Sort_String)
  
EndProcedure

Procedure.S FolderSelect(astrFolder.S)
  ; Displays a path requester and returns the new path, or the old one if the requester is cancelled.
  
  Protected.S strSelect
  
  strSelect = PathRequester("Choose a folder.", astrFolder)
  
  If strSelect = ""
    strSelect = astrFolder
  EndIf
  
  ProcedureReturn strSelect
  
EndProcedure

Procedure LabelUpdate(astrFolder.S)
  ; Updates the folder label.
  
  SetGadgetText(#txtFolder, astrFolder)
  
EndProcedure

Procedure ListLoad(alngListView.L, List alstFiles.FILEITEM())
  ; Load the files properties from lstFiles into the list view lngListView.
  
  Protected.S strAccess, strAttrib, strCreate, strFolder, strModify, strMsg, strNum, strSize
  
  ; Remove previous contents.
  ClearGadgetItems(alngListView)
  
  ForEach alstFiles()
    
    ; Display the item number and file name.
    strNum = StrU(ListIndex(alstFiles()) + 1)
    
    ; These lines convert the three date values to something more familiar.
    strCreate = FormatDate("%dd/%mm/%yyyy", alstFiles()\DateCreated)
    strAccess = FormatDate("%dd/%mm/%yyyy", alstFiles()\DateAccessed)
    strModify = FormatDate("%dd/%mm/%yyyy", alstFiles()\DateModified)
    
    ; Convert the file size to a padded string the same as with the index value above,
    ; but allow space for the maximum size of a quad.
    strSize = StrU(alstFiles()\Size)
    
    ; Convert the attributes to a string, for now.
    strAttrib = StrU(alstFiles()\Attributes)
    
    ; Build a row string.  
    ; The Line Feed character 'Chr(10)' tells the gadget to move to the next column.
    strMsg = strNum + Chr(10) + alstFiles()\Name + Chr(10) + strCreate + Chr(10) + strAccess + Chr(10) + strModify + Chr(10) + strAttrib + Chr(10) + strSize
    
    ; Add the row to the list view gadget.
    AddGadgetItem(#lsiFiles, -1, strMsg)
    
  Next alstFiles()
  
EndProcedure

Procedure WindowCreate()
  ; Creates the wdwFiles window.
  
  Protected.L lngFlags
  
  ; This line defines a flag for the window attributes by OR-ing together the desired attribute constants.
  lngFlags = #PB_Window_SystemMenu |#PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget| #PB_Window_TitleBar
  
  ; Open a window.
  OpenWindow(#wdwFiles, 50, 50, 400, 400, "File Properties", lngFlags)
  ; A button to choose a folder.
  ButtonGadget(#btnFolder, 5, 5, 100, 30, "Select Folder")
  ; A button to update the list.
  ButtonGadget(#btnUpdate, 105, 5, 100, 30, "Update List")
  ; A text gadget to show the name of the folder.
  TextGadget(#txtFolder, 5, 40, 390, 25, "")
  ; A list icon gadget to hold the file list and properties.
  ListIconGadget(#lsiFiles, 5, 70, 390, 326, "#", 30)
  ; Add columns to the ListIconGadget to hold each property.
  AddGadgetColumn(#lsiFiles, 1, "Name", 200)
  AddGadgetColumn(#lsiFiles, 2, "Created", 90)
  AddGadgetColumn(#lsiFiles, 3, "Accessed", 90)
  AddGadgetColumn(#lsiFiles, 4, "Modified", 90)
  AddGadgetColumn(#lsiFiles, 5, "Attributes", 150)
  AddGadgetColumn(#lsiFiles, 6, "Size", 150)
  
EndProcedure

Procedure WindowDestroy()
  ; Closes the window.
  ; If necessary, you could do other tidying up jobs here too.
  
  CloseWindow(#wdwFiles)
  
EndProcedure

Procedure WindowResize()
  ; Resizes window gadgets to match the window size.
  
  ResizeGadget(#txtFolder, #PB_Ignore, #PB_Ignore, WindowWidth(#wdwFiles) - 10, #PB_Ignore)
  ResizeGadget(#lsiFiles, #PB_Ignore, #PB_Ignore, WindowWidth(#wdwFiles) - 10, WindowHeight(#wdwFiles) - 74)
  
EndProcedure

;- Main
; Now we define a list of files using the structure previously specified.
NewList lstFiles.FILEITEM()

; And some working variables to make things happen.
Define.S strFolder
Define.L Event, EventWindow, EventGadget, EventType, EventMenu

; This function gets the home directory for the logged on user.
strFolder = GetHomeDirectory()

; Create the window and set the initial contents.
WindowCreate()
WindowResize()
LabelUpdate(strFolder)
FilesExamine(strFolder, lstFiles())
ListLoad(#lsiFiles, lstFiles())

;- Event Loop
Repeat
  
  ; Wait until a new window or gadget event occurs.
  Event = WaitWindowEvent()
  EventWindow = EventWindow()
  EventGadget = EventGadget()
  EventType = EventType()
  
  ; Take some action.
  Select Event
      
    Case #PB_Event_Gadget
      ; A gadget event occurred.
      If EventGadget = #btnFolder
        ; The folder button was clicked.
        strFolder = FolderSelect(strFolder)
        LabelUpdate(strFolder)
        FilesExamine(strFolder, lstFiles())
        ListLoad(#lsiFiles, lstFiles())
        
      ElseIf EventGadget = #btnUpdate
        ; The update button was clicked.
        FilesExamine(strFolder, lstFiles())
        ListLoad(#lsiFiles, lstFiles())
        
      ElseIf EventGadget = #txtFolder
        ; Do nothing here.
        
      ElseIf EventGadget = #lsiFiles
        ; Do nothing here.
        
      EndIf
      
    Case #PB_Event_SizeWindow
      ; The window was moved or resized.
      If EventWindow = #wdwFiles
        WindowResize()  
      EndIf
      
    Case #PB_Event_CloseWindow
      ; The window was closed.
      If EventWindow = #wdwFiles
        WindowDestroy()
        Break
        
      EndIf
      
  EndSelect
  
ForEver
Previously, we mentioned four limitations to this program. This new version uses procedures to address three of them.

1) You couldn't choose a folder to show.
The "FolderSelect" procedure shows a path requester to allow the user to select a folder. The variable "strFolder" is updated with the result of this procedure. The button also calls "LabelUpdate", "FilesExamine" and "ListLoad" to display the contents of the new folder in the window.

2) You can't update the list contents without closing and restarting the program.
Now, when the "Update List" button is clicked, "FilesExamine" and "ListLoad" are called again to update the display.

3) If you resize the window, the gadgets don't resize with it.
The "WindowResize" procedure is called in the event loop to resize the gadgets when the form is resized. Also, although this program didn't really need to, but this procedure is called after calling "WindowCreate" to make sure the gadgets are the right size initially.

Notice how several of the procedures are called more than once to perform similar but not identical functions. This improves the efficiency of the program.

We have one final limitation to overcome in a later item.
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2056
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

spikey wrote:@Andre:
That location is fine - while writing the GUI item it occurred to me that I shouldn't start using Enumerations without introducing them properly first.

However I see that I made a couple of mistakes in that example, it won't compile as it stands! :oops: I've corrected my post now.
Thank you very much for your work, again! :D

According to your latest contributions I've updated the 'Constants' topic, and have added the content for the 'Structuring code in procedures' and 'Simple 2D Drawing...' topics.


@bembulak: thanks for your offer! Similar to the offer of bluenzl we come back to it later, if it's still needed after adding the current contributions. :)
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Compiler Directives item

Post by spikey »

This will be our last visit to the File Properties program. There is one limitation discussed previously to overcome and we've left it until now because it is a special case.

So far the Attributes column on the display has simply been an integer. This is because the return values of the GetFileAttributes and DirectoryEntryAttributes instructions have a different meaning on Windows systems compared with Mac and Linux systems.

We can't allow for this difference at run-time, however we can use Compiler Directives to have the program behave differently on the three different operating systems.

Add this new procedure declaration to that section.

Code: Select all

Declare.S AttributeString(alngAttributes.L)
Add this new procedure to the implementation section.

Code: Select all

Procedure.S AttributeString(alngAttributes.L)
  ; Converts an integer attributes value into a string description.
  ; Supports Linux, Mac and Windows system's attributes.
  
  Protected.S strResult
  
  strResult = ""
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    
    ; These are the attributes for Windows systems.
    ; A logical-and of the attribute with each constant filters out that bit and can then be used for comparison.

    If alngAttributes & #PB_FileSystem_Archive
      strResult + "A"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_Compressed
      strResult + "C"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_Hidden
      strResult + "H"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_ReadOnly
      strResult + "R"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_System
      strResult + "S"
    Else
      strResult + " "
    EndIf
    
  CompilerElse
    
    ; These are the attributes for Mac and Linux systems.
    
    If alngAttributes & #PB_FileSystem_Link
      strResult + "L "
    Else
      strResult + "  "
    EndIf
    
    ; User attributes.
    If alngAttributes & #PB_FileSystem_ReadUser
      strResult + "R"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_WriteUser
      strResult + "W"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_ExecUser
      strResult + "X "
    Else
      strResult + "  "
    EndIf
    
    ; Group attributes.
    If alngAttributes & #PB_FileSystem_ReadGroup
      strResult + "R"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_WriteGroup
      strResult + "W"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_ExecGroup
      strResult + "X "
    Else
      strResult + "  "
    EndIf
    
    ; All attributes.
    If alngAttributes & #PB_FileSystem_ReadAll
      strResult + "R"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_WriteAll
      strResult + "W"
    Else
      strResult + " "
    EndIf
    
    If alngAttributes & #PB_FileSystem_ExecAll
      strResult + "X"
    Else
      strResult + " "
    EndIf
    
  CompilerEndIf
  
  ; Return the result.
  ProcedureReturn strResult
  
EndProcedure
Finally, replace these two lines in the ListLoad procedure

Code: Select all

    ; Convert the attributes to a string, for now.
    strAttrib = StrU(alstFiles()\Attributes)
with these,

Code: Select all

    ; Call AttributeString to convert the attributes to a string representation.
    strAttrib = AttributeString(alstFiles()\Attributes)
Now when the program is executed a string display will be shown instead of the integer values.
On a Windows system it would look something like this (assuming all attributes are set):

Code: Select all

ACHRS
and on the other two systems:

Code: Select all

L RWX RWX RWX
The "CompilerIf" instruction works much like an "If" instruction - however it is the compiler that makes the decision at compile-time, rather than the executable at run-time. This means that we can include different code to handle the file attributes on the different operating systems.

The lines between:

Code: Select all

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
and

Code: Select all

CompilerElse
will be compiled on Windows systems. The constant #PB_Compiler_OS is automatically defined by PureBasic to allow this kind of program logic.

The other section will be used on Mac and Linux systems - which work the same way, conveniently. If these operating systems had different attribute values too, then we could use "CompilerSelect" and "CompilerCase" to make a three-way determination.

Code: Select all

CompilerSelect #PB_Compiler_OS
    
  CompilerCase #PB_OS_Linux
    ; Code for Linux systems.
    
  CompilerCase #PB_OS_MacOS
    ; Code for Mac systems.
    
  CompilerCase #PB_OS_Windows
    ; Code for Windows systems.
    
CompilerEndSelect
The last compiler directive that we're going to discuss here is:

Code: Select all

EnableExplicit.
There is a good reason for using this directive. It requires that all variables must be defined explicitly before usage, in some way, (using Define, Dim, Global, Protected, Static etc.) Doing so eliminates the possibility of logic errors due to mistyped variable names being defined "on-the-fly". This type of subtle error will not affect a program's compilation but could well present as an inconvenient bug at run-time. Using this directive eliminates this kind of problem as a compiler error would occur.

For example:

Code: Select all

EnableExplicit

Define.L lngField, lngFieldMax

; ...

If lngField < lngFeildMax
  ; If EnableExplicit is omitted this section of code may not execute when intended because lngFeildMax will be zero.
EndIf
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2056
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by Andre »

@spikey: thanks, I've added your latest contribution as (new) "Compiler directives..." topic!
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by IdeasVacuum »

I think the following snippet by nco2k on how to use CatchImage() belongs in the help. Though we might say it's using advanced methods, image handling is fundamental in all types of apps, something you do need to know how to handle early on:

Code: Select all

#Win    = 1
#imGadg = 2
#Image  = 3

If OpenWindow(#Win, 0, 0, 256, 256, "Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

         If CatchImage(#Image, ?Image_Start, ?Image_End - ?Image_Start)

                  ImageGadget(#imGadg, 0, 0, ImageWidth(#Image), ImageHeight(#Image), ImageID(#Image))

                  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow

         EndIf

EndIf

DataSection
  Image_Start:
    IncludeBinary #PB_Compiler_Home + "Examples\Sources\Data\Background.bmp"
  Image_End:
EndDataSection
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt

Post by spikey »

@Andre:
Oh OK. I was assuming that's what you meant when you put "Compiler commands" in your proposed table of contents! What did you have in mind then?
Post Reply