It is currently Mon Dec 11, 2017 5:10 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 116 posts ]  Go to page 1, 2, 3, 4, 5 ... 8  Next
Author Message
 Post subject: PureBasic Docs- Ideas/Help needed for a "We start" chapter!?
PostPosted: Sun Mar 27, 2011 10:08 am 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 6:14 pm
Posts: 1391
Location: Germany (Saxony, Deutscheinsiedel)
Hi,

months ago I had the idea of including a short "We start" chapter into the PB docs.

While the (freely available, now in english and also german) "PureBasic Book" of Kale is a very good tutorial and has a lot of informations for new (and also advanced) PB user, I thought the PB manual should get a beginners page too! :D
It should be a much shorter (all on one page!) than Kale's book, but use the adavantage of being included in the PB manual itself, so it can link directly to the related pages (command, keyword, ...) in the PB manual and so guide the (beginner) user to further informations. So it's main goal is, to provide the user a better start and guide him through the many manual descriptions...

Unfortunately because of lack of time it didn't progress very much.

So my questions are:
- What do you think about this idea?
- Anyone wants to help creating such a chapter? If yes, just take one topic (e.g. possible "loops" in PureBasic) and post your suggestion in this thread...

Here is what I have so far:
(already included links to other pages in PB manual are not shown here, but can be added by me when including it in PB manual, as well the whole layout according to PB's docmaker syntax)

Last edited: 21th-May-2012

Current status of the project, when included into the reference manual of PureBasic:
=> http://www.purearea.net/pb/english/manu ... rview.html

This is Part 1 - see also Part 2

Quote:
We start programming...

Overview

The following topics in this chapter should give you some ideas, where to start with PureBasic. It shouldn't replace any larger tutorial or the massive informations, which can be found on the popular PureBasic forums. So the following information texts are short, but they include some "keywords" and links to futher informations, which are included in this reference manual. This chapter covers only a small part of the 1100+ commands available in PureBasic! :)

Topics in this chapter:
- First steps
- Variables and Processing of variables
- Constants
- Decisions & Conditions
- Loops
- String Manipulation [missing]
- Storing data in memory
- Input & Output
- Displaying text output (Console)
- Building a graphical user interface (GUI)
- Displaying graphics output & simple drawing
- Structuring code in Procedures
- Compiler directives (for different behaviour on different OS)
- Reading and writing files
- Memory access
- Other Compiler keywords [missing]
- Other Library functions [missing]
- Advanced functions [missing]
- Some Tipps & Tricks [one example until now]
- ......

First steps with the Debug output (variables & operators)
Normally we would present here the typical "Hello World". You want to see it? Ok here we go with two examples:

"Hello World" in the Debug output window:

Code:
Debug "Hello World!"


"Hello World" in a MessageRequester:

Code:
MessageRequester("", "Hello World!")


We now continue with a short example using the available variable types, arithmetic operators and displaying the result:

Code:
      a = 5
      b = 7
      c = 3
     
      d = a + b   ; we use the values stored in variables 'a' and 'b' and save the sum of them in 'd'
      d / c       ; we directly use the value of 'd' (= 12) divided by the value of 'c' (= 3) and save the result in 'd'
     
      Debug d    ; will give 4

This way you have used variables "on the fly". To force the compiler always declaring variables before their first use, just use the keyword EnableExplicit.


Variables and Processing of variables

spikey wrote:
A is an integer - but note that you can't declare variables this way if you use the EnableExplicit directive.
Outside of a procedure A will exist at Main scope, within a procedure it will become local.
Code:
A = 0


B is a long integer, C is a floating point number, they will be initialised to zero.
Code:
Define B.L, C.F


D and E are long integers too. However, they will be initialised to the stated constants. Notice too the alternative syntax of Define used.
Code:
Define.L D = 10, E = 20


F is a string.
Code:
Define.S F


So is G$, however, if you declare strings this way, you must always use the $ notation.
Code:
G$ = "Hello, "


This won't work. (G becomes a new integer variable and a compiler error occurs).
Code:
G = "Goodbye, World!"


H is an array of 20 strings, array indexing begins at zero.
Code:
Dim H.S(19)


Now H is an array of 25 strings. If the array is resized larger, its original contents will be preserved.
Code:
ReDim H.S(24)


J will appear at Global, rather than Main, scope. It will appear in all procedures, but maybe a better way would be to use the Shared keyword inside a procedure on a main scope variable, so that the chances of accidental changes are minimized.
Code:
Global.I J


K will be a new, empty, list of strings at main scope.
Code:
NewList K.S()


M will be a new, empty, map of strings at main scope.
Code:
NewMap M.S()


Note that you can't use the alternative syntax of the Define keyword with NewList or NewMap though. A compiler error will result.

Within a procedure.
Code:
Procedure TestVariables()
 
  ; N and P will be local.
  Define.L N
  Protected.L P
 
  ; Q will be a static local.
  Static.L Q
 
  ; The main scope variable F and the string list K will be available within this procedure.
  Shared F, K()
 
  ; The global scope variable J will be available here implicitly.
 
EndProcedure


Using operators on variables.
Code:
; Add two to A.
A + 2
; Bitwise Or with 21 (A will become 23)
A | 21
; Bitwise And with 1 (A will become 1)
A & 1
; Arithmetic shift left (A will become 2, that is 10 in binary).
A << 1


String concatenation.
Code:
G$ + "World!"


Add an element to the K list.
Code:
AddElement(K())
K() = "List element one"


Add an element to the M map.
Code:
M("one") = "Map element one"



Constants

spikey wrote:
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:
#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:
Enumeration
  #MyConstantA
  #MyConstantB
  #MyConstantC
EndEnumeration

Enumeration 10 Step 5
  #MyConstantD ; will be 10
  #MyConstantE ; will be 15
  #MyConstantF ; will be 20
EndEnumeration



Decisions and Conditions

spikey wrote:
There are different ways of processing data obtained from user input or other way (loading from a file, ...). The common arithmetic functions (+, -, *, /, ...) can be combined with conditions. You can use the "If : Else/ElseIf : EndIf" set of keywords or the the "Select : Case/Default : EndSelect" keywords, just use what is the best for your situation!

This example shows the use of "If : ElseIf : Else : EndIf" creates a message, possibly for showing in the status bar of a form or something similar, based upon the number of items and filtered items in an, imaginary, list. Note that unlike some other BASIC languages, PureBasic doesn't use the "Then" keyword and that there is no space in the ElseIf and EndIf keywords.

Code:
Define.L lItems = 10, lFilter = 6
Define.S sMsg

If lItems = 0
  sMsg = "List is empty."
 
ElseIf lItems = 1 And lFilter = 0
  sMsg = "One item. Not shown by filter."
 
ElseIf lItems > 1 And lFilter = 0
  sMsg = StrU(lItems) + " items. All filtered."
 
ElseIf lItems > 1 And lFilter = 1
  sMsg = StrU(lItems) + " items. One shown by filter."
 
ElseIf lItems = lFilter
  sMsg = StrU(lItems) + " items. None filtered."
 
Else
  ; None of the other conditions were met.
  sMsg = StrU(lItems) + " items. " + StrU(lFilter) +" shown by filter."
 
EndIf

Debug sMsg



This example shows the use of Select : Case : Default : EndSelect to categorize the first 127 ASCII characters into groups. The "For : Next" loop counts to 130 to demonstrate the Default keyword.

Code:
Define.C cChar
Define.S sMsg

For cChar = 0 To 130
 
  Select cChar
     
    Case 0 To 8, 10 To 31, 127
      sMsg = StrU(cChar) + " is a non-printing control code."
     
    Case 9
      sMsg = StrU(cChar) + " is a tab."
     
    Case 32
      sMsg = StrU(cChar) + " is a space."
     
    Case 36, 128   
      sMsg = StrU(cChar) + " is a currency symbol. (" + Chr(cChar) + ")"
     
    Case 33 To 35, 37 To 47, 58 To 64, 91 To 96
      sMsg = StrU(cChar) + " is a punctuation mark or math symbol. (" + Chr(cChar) + ")"
     
    Case 48 To 57
      sMsg = StrU(cChar) + " is a numeral. (" + Chr(cChar) + ")"
     
    Case 65 To 90
      sMsg = StrU(cChar) + " is an upper case letter. (" + Chr(cChar) + ")"
     
    Case 97 To 122
      sMsg = StrU(cChar) + " is a lower case letter. (" + Chr(cChar) + ")"
     
    Default
      ; If none of the preceding Case conditions are met.
      sMsg = "Sorry, I don't know what " + StrU(cChar) + " is!"
     
  EndSelect
 
  Debug sMsg
 
Next cChar



Loops

spikey wrote:
Data, Events or many other things can also be processed using loops, which are always checked for a specific condition. Loops can be: "Repeat : Until", "Repeat : Forever", "While : Wend", "For : Next", "ForEach : Next".

In this loop the counter A is increased by one each time, this loop will always perform the same number of iterations.

Code:
Define.I A
For A = 0 To 10 Step 2
  Debug A
Next A


This loop will increment the variable B by a random amount between 0 and 20 each time, until B exceeds 100. The number of iterations actually performed in the loop will vary depending on the random numbers. The check is performed at the start of the loop - so if the condition is already true, zero iterations may be performed. Take the ; away from the second line to see this happen.

Code:
Define.I B
;B = 100
While B < 100
  B + Random(20)
  Debug B
Wend


This loop is very similar to the last except that the check is performed at the end of the loop. So one iteration, at least, will be performed. Again remove the ; from the second line to demonstrate.
Code:
Define.I C
; C = 100
Repeat
  C + Random(20)
  Debug C
Until C > 99


This loop is infinite. It won't stop until you stop it (use the red X button on the IDE toolbar).
Code:
Define.I D
Repeat
  Debug D
Forever


There is a special loop for working with linked lists and maps, it will iterate every member of the list (or map) in turn.

Code:
NewList Fruit.S()

AddElement(Fruit())
Fruit() = "Banana"

AddElement(Fruit())
Fruit() = "Apple"

AddElement(Fruit())
Fruit() = "Pear"

AddElement(Fruit())
Fruit() = "Orange"

ForEach Fruit()
  Debug Fruit()
Next Fruit()



Storing data in memory

spikey wrote:
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:
; 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 lstFiles.FILEITEM()
Define.S strFolder
Define.L lngResult

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

; Open the directory to enumerate all 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

; If there are some entries in the list, show the results in the debug window.
If ListSize(lstFiles())
 
  ForEach lstFiles()
   
    Debug "Filename = " + lstFiles()\Name
    Debug "Size = " + Str(lstFiles()\Size)
    Debug "Attributes = " + StrU(lstFiles()\Attributes)
    Debug "Created = " + StrU(lstFiles()\DateCreated)
    Debug "Accessed = " + StrU(lstFiles()\DateAccessed)
    Debug "Modified = " + StrU(lstFiles()\DateModified)
   
  Next lstFiles()
 
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:
...
Debug "Created = " + FormatDate("%dd/%mm/%yyyy", lstFiles()\DateCreated)
Debug "Accessed = " + FormatDate("%dd/%mm/%yyyy", lstFiles()\DateAccessed)
Debug "Modified = " + FormatDate("%dd/%mm/%yyyy", lstFiles()\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:
...

; Sort the list into ascending alphabetical order of file name.
SortStructuredList(lstFiles(), #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).



Input & Output

Every PureBasic application can communicate and interact with the user on different ways.

Thereby we distinguish between

a) the pure output of informations
b) the interaction of the PureBasic application with the user, when user-input will be taken and the results will be outputted again.

It's not possible anymore, to use a simple "PRINT" command to output some things on the screen, like it was possible on DOS operating systems (OS) without a graphical user interface (GUI) years ago. Today such a GUI is always present, when you use an actual OS like Windows, Mac OSX oder Linux.

For the output of informations we have different possibilities:

- debug window (only possible during programming with PureBasic)
- MessageRequester (output of shorter text messages in a requester window)
- files (for saving the results in a text-file, etc.)
- console (for simple and almost non-graphic text output, most similar to earlier DOS times)
- windows and gadgets (standard windows with GUI elements on the desktop of the OS, e.g. for applications)
- Screen (Output of text and graphics directly on the screen, e.g. for games)

To be able to record and process input by the user of the PureBasic application, the three last-mentioned output options have also the possibility to get user-input:

- in the console using Input()
- in the window using WaitWindowEvent() / WindowEvent(), which can get the events occured in the window, like clicking on a button or the entered text in a StringGadget
- in the graphics screen using the keyboard
- the is as well the possibility to get user-input using the InputRequester


Displaying text output

Andre wrote:
This part of text can be deleted later, I think.... as it will be replaced by spikey's contribution! :-)

In previous chapter "Input & Output" you already got an overview about the different possibilities to output text to the user.

First we will store some data in memory, which will be used later in the output examples:

....

And now we will see several examples of "How to output" text (using the previous stored data):

.....



spikey wrote:
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:
...

; Now we define a list of files using the structure previously specified.
NewList lstFiles.FILEITEM()
Define.S strAccess, strAttrib, strCreate, strFolder, strModify, strMsg, strNum, strSize
Define.L lngResult

...


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

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


Now replace this with:-

Code:

; 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 " + strFolder + ".")
PrintN("-------------------------------------------------------------------------------")
strMsg = "Num Name"
PrintN(strMsg)
strMsg = Space(4) + "Create" + Space(5) + "Access" + Space(5) + "Modify" + Space(5) + "Attrib Size"
PrintN(strMsg)

; If there are some entries in the list, show the results in the console window.
If ListSize(lstFiles())
 
  ; Loop through the linked list to display the results.
  ForEach lstFiles()
   
    ; 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.
    strNum = RSet(StrU(ListIndex(lstFiles()) + 1), 3, " ")
   
    ; Display the item number and file name.
    strMsg = strNum + " " + lstFiles()\Name
    PrintN(strMsg)
   
    ; 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 = RSet(StrU(lstFiles()\Size), 19)
   
    ; Convert the attributes to a string, for now.
    strAttrib = RSet(StrU(lstFiles()\Attributes), 6, " ")
   
    ; Display the file's properties.
    strMsg = Space(4) + strCreate + " " + strAccess + " " + strModify + " " + strAttrib + " " + strSize
    PrintN(strMsg)
   
    ; Display a blank line.
    PrintN("")
   
  Next lstFiles()
 
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:
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.



Building a graphical user interface (GUI)

spikey wrote:
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:
; 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.



Displaying graphics output & simple drawing

spikey wrote:
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:
; 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



Structuring code in Procedures

spikey wrote:
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:
; 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.



Compiler directives (for different behaviour on different OS)

spikey wrote:
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:
Declare.S AttributeString(alngAttributes.L)


Add this new procedure to the implementation section.
Code:
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:
    ; Convert the attributes to a string, for now.
    strAttrib = StrU(alstFiles()\Attributes)

with these,
Code:
    ; 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:
ACHRS

and on the other two systems:
Code:
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:
CompilerIf #PB_Compiler_OS = #PB_OS_Windows

and
Code:
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:
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:
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:
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



Reading and writing files

spikey wrote:
This example will write 100 random records each containing a byte, a floating point number, a long integer and a string. It then reads all the records back and displays them in the debug window.

It demonstrates the GetTemporaryDirectory, CreateFile, OpenFile, EOF and a number of Read and Write data instructions too.

It works fine as far as it goes, but has a drawback. As the string value has a variable length - you can't randomly access the records because you can't predict where each new record will start in the file. They must be all be read back in the same sequence as they were written. This isn't a problem with the small number of records created here but this could be an inconvenience with a larger file. PureBasic offers a way to handle this situation too - but an example would be too complex for this topic. See the "Database" sections of the help file or reference manual to see how it could be done.

Code:
; Define some variables.
Define.F fltFloat
Define.L lngCount, lngFile
Define.S strFolder, strFile, strString

; Create a temporary file name.
strFolder = GetTemporaryDirectory()
strFile = strFolder + "test.data"

; Create the temporary file.
; If #PB_Any is used, CreateFile returns the file's number. 
; Useful if you may have more than one file open simultaneously.
lngFile = CreateFile(#PB_Any, strFile)

If lngFile
  ; If this was successful - write 100 random records.
  For lngCount = 1 To 100
   
    ; Write a random byte (0 - 255).
    WriteByte(lngFile, Random(#MAXBYTE))
   
    ; Create and write a random float.
    ; This calculation is there to make the number have a floating point component (probably).
    fltFloat = Random(#MAXLONG) / ((Random(7) + 2) * 5000)
    WriteFloat(lngFile, fltFloat)
   
    ; Write a random long.
    WriteLong(lngFile, Random(#MAXLONG))
   
    ; Create and write a random string in Unicode format.
    ; Note the use of WriteStringN to delimit the string with an end of line marker.
    strString = "String " + StrU(Random(#MAXLONG))
    WriteStringN(lngFile, strString, #PB_Unicode)
   
  Next lngCount
 
  ; Close the file.
  CloseFile(lngFile)
 
Else
  ; If this was unsuccessful.
  Debug "Could not create the file: " + strFile
 
EndIf

; Open the file for reading this time.
lngFile = ReadFile(#PB_Any, strFile)

If lngFile
  ; If this was successful - read and display records from the file.

  ; Reset the counter.
  lngCount = 1
 
  ; Loop until the 'end of file' is reached.
  ; This will read all of the records regardless of how many there are.
  While Eof(lngFile) = 0
   
    ; Print a header line.
    Debug "------------------------------------------------------------------------------------------------"
   
    Debug "[" + StrU(lngCount) + "]"
    lngCount + 1
    ; Read a byte value and print it.
    Debug StrU(ReadByte(lngFile), #PB_Byte)
   
    ; Read a float value..
    Debug StrF(ReadFloat(lngFile))
   
    ; Read a long value.
    Debug StrU(ReadLong(lngFile), #PB_Long)
   
    ; Read a string value.
    Debug ReadString(lngFile, #PB_Unicode)
   
  Wend
 
  ; Print the trailing line.
  Debug "------------------------------------------------------------------------------------------------"
   
  ; Close the file.
  CloseFile(lngFile)
 
  ; Tidy up.
  DeleteFile(strFile)
 
Else
  ; If this was unsuccessful.
  Debug "Could not open the file: " + strFile
 
EndIf



Memory access
...
and further topics see Part 2

_________________
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)


Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Mon Mar 28, 2011 12:44 am 
Offline
Always Here
Always Here

Joined: Fri Oct 23, 2009 2:33 am
Posts: 5695
Location: Wales, UK
Certainly is a very good idea Andre, many newbies are 'lost' to begin with (I was).

For obvious reasons, the majority of examples on the forum tend to be biased towards Windows OS.

I'd say keep the bulk of the chapter OS neutral, maybe introduce the supported platforms and the ramifications of cross-platform programming last.

_________________
IdeasVacuum
If it sounds simple, you have not grasped the complexity.


Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Mon Mar 28, 2011 2:23 pm 
Offline
User
User

Joined: Sat Oct 23, 2010 8:51 am
Posts: 50
Location: UK
Good idea!

It would be worth highlighting differences between PB and more traditional BASICs, especially related to syntax.

Things like MID$, LEFT$, RIGHT$ becoming MID, LEFT, RIGHT

(though, granted it's not too difficult to find it out)

However, I would have appreciated something more obvious when I first started using PB's DATA statements.

I remember getting a strange error which I couldn't find in the documentation (and can't remember now) and wishing I could find a complete and comprehensive list of error messages. But more-so, would have liked something saying:

When you use strings in your DATA statements, you must append '.s' to both the READ and DATA commands

I know it's in the documentation, and when I read it now, I feel ashamed I didn't see it sooner :oops: but there we go, I've used DATA countless times in other BASICs but got caught out here. I think the biggest problem was the strange error that didn't really help. If it had said 'Missing variable type in DATA statement', I'd have been fine :D

In fact, it would be good to have one page per command in the help. Going back to DATA it is on a page with a lot of other (albeit relevant) information and you don't see the full syntax until you scroll down and you've got to go down the bottom to see the stuff about appending the variable etc. Maybe I'm just being picky :D


Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Mon Mar 28, 2011 7:21 pm 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 6:14 pm
Posts: 1391
Location: Germany (Saxony, Deutscheinsiedel)
Thanks to you too, for your suggestions! :D

Of course this chapter should be OS-neutral, so no OS-specific examples etc...
The (main) differences between the OSes supported by PureBasic are maybe content of another chapter in the manual. But that's not the goal here.

Maybe the "We start" chapter should include some basic comparisons to other BASICs. But generally I think that's not the focus of a reference manual. For such things the (now freely available) book of Kale (see link in my first post) is the right thing.

Main goal of this (not too long) chapter should be, to give the newbie a start and guide him/her to other pages of the manual. Like I did with the already written "Input & Output" section - there the user gets an overview about what's possible and will be linked to the other pages of the manual.

More suggestions, or if anyone wants also contributions, are very welcome! :)
Thanks!

_________________
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)


Top
 Profile  
Reply with quote  
 Post subject: Loops Item
PostPosted: Mon Mar 28, 2011 8:01 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Wed Sep 22, 2010 1:17 pm
Posts: 250
Location: United Kingdom
Data, Events or many other things can also be processed using loops, which are always checked for a specific condition. Loops can be: "Repeat : Until", "Repeat : Forever", "While : Wend", "For : Next", "ForEach : Next".

In this loop the counter A is increased by one each time, this loop will always perform the same number of iterations.

Code:
Define.I A
For A = 0 To 10 Step 2
  Debug A
Next A


This loop will increment the variable B by a random amount between 0 and 20 each time, until B exceeds 100. The number of iterations actually performed in the loop will vary depending on the random numbers. The check is performed at the start of the loop - so if the condition is already true, zero iterations may be performed. Take the ; away from the second line to see this happen.

Code:
Define.I B
;B = 100
While B < 100
  B + Random(20)
  Debug B
Wend


This loop is very similar to the last except that the check is performed at the end of the loop. So one iteration, at least, will be performed. Again remove the ; from the second line to demonstrate.
Code:
Define.I C
; C = 100
Repeat
  C + Random(20)
  Debug C
Until C > 99


This loop is infinite. It won't stop until you stop it (use the red X button on the IDE toolbar).
Code:
Define.I D
Repeat
  Debug D
Forever


There is a special loop for working with linked lists and maps, it will iterate every member of the list (or map) in turn.

Code:
NewList Fruit.S()

AddElement(Fruit())
Fruit() = "Banana"

AddElement(Fruit())
Fruit() = "Apple"

AddElement(Fruit())
Fruit() = "Pear"

AddElement(Fruit())
Fruit() = "Orange"

ForEach Fruit()
  Debug Fruit()
Next Fruit()


Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Tue Mar 29, 2011 7:34 pm 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 6:14 pm
Posts: 1391
Location: Germany (Saxony, Deutscheinsiedel)
@spikey: well done, a good introduction for the "Loops" topic! I've included your contribution in my first post. Thank you :D

Btw.: All links to other chapters of the PB manual (the used keywords + commands) I can add, when including it in the manual.

Any further suggestions + contributions are very welcome! :mrgreen:

_________________
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)


Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Tue Mar 29, 2011 11:21 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Tue Dec 08, 2009 8:34 pm
Posts: 161
Location: United Kingdom
Why not just update Kale's book and include that into the main PB package? It seems your re-inventing the wheel here.

_________________
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto is never necessary, and in practice it is almost always easy to write code without it. We have not used goto in this book. -- K&R (2nd Ed.) : Page 65


Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Wed Mar 30, 2011 7:38 pm 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 6:14 pm
Posts: 1391
Location: Germany (Saxony, Deutscheinsiedel)
Blood wrote:
Why not just update Kale's book and include that into the main PB package? It seems your re-inventing the wheel here.


That's no real option.

First point: Kale's book is under the Creative Commons licence, so it can't be included into the PB manual, which is part of a commercial/shareware package.

Second point: this "We start" chapter should be a short overview to give the PB user a (better) start into the language and include links to further information already included in the PB manual. So this chapter should never become a competitor to Kale's 400 site book!

_________________
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)


Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Wed Mar 30, 2011 9:27 pm 
Offline
Addict
Addict

Joined: Mon Aug 04, 2008 10:56 pm
Posts: 1100
Location: Seattle, USA
Looks like a big job!

_________________
MacBook Pro-r, OSX 10.11.6, Command Line Tools 7.3 (10.11), PB-5.60x64


Last edited by WilliamL on Fri May 06, 2011 9:13 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Thu Mar 31, 2011 5:10 pm 
Offline
PureBasic Expert
PureBasic Expert
User avatar

Joined: Sat May 17, 2003 11:31 am
Posts: 6032
I'm against it.

:mrgreen:

_________________
( PB5.50 Win7 x64 Dell XPS710 nForce 5 Intel Q6600 SSD Evo 840 GFX GTS450 )
( You have two options: psychotherapy, or the PureBasic Survival Guide... )


Top
 Profile  
Reply with quote  
 Post subject: Variables and Processing of Variables Item
PostPosted: Thu Mar 31, 2011 6:48 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Wed Sep 22, 2010 1:17 pm
Posts: 250
Location: United Kingdom
A is an integer - but note that you can't declare variables this way if you use the EnableExplicit directive.
Outside of a procedure A will exist at Main scope, within a procedure it will become local.
Code:
A = 0


B is a long integer, C is a floating point number, they will be initialised to zero.
Code:
Define B.L, C.F


D and E are long integers too. However, they will be initialised to the stated constants. Notice too the alternative syntax of Define used.
Code:
Define.L D = 10, E = 20


F is a string.
Code:
Define.S F


So is G$, however, if you declare strings this way, you must always use the $ notation.
Code:
G$ = "Hello, "


This won't work. (G becomes a new integer variable and a compiler error occurs).
Code:
G = "Goodbye, World!"


H is an array of 20 strings, array indexing begins at zero.
Code:
Dim H.S(19)


Now H is an array of 25 strings. If the array is resized larger, its original contents will be preserved.
Code:
ReDim H.S(24)


J will appear at Global, rather than Main, scope. It will appear in all procedures, but maybe a better way would be to use the Shared keyword inside a procedure on a main scope variable, so that the chances of accidental changes are minimized.
Code:
Global.I J


K will be a new, empty, list of strings at main scope.
Code:
NewList K.S()


M will be a new, empty, map of strings at main scope.
Code:
NewMap M.S()


Note that you can't use the alternative syntax of the Define keyword with NewList or NewMap though. A compiler error will result.

Within a procedure.
Code:
Procedure TestVariables()
 
  ; N and P will be local.
  Define.L N
  Protected.L P
 
  ; Q will be a static local.
  Static.L Q
 
  ; The main scope variable F and the string list K will be available within this procedure.
  Shared F, K()
 
  ; The global scope variable J will be available here implicitly.
 
EndProcedure


Using operators on variables.
Code:
; Add two to A.
A + 2
; Bitwise Or with 21 (A will become 23)
A | 21
; Bitwise And with 1 (A will become 1)
A & 1
; Arithmetic shift left (A will become 2, that is 10 in binary).
A << 1


String concatenation.
Code:
G$ + "World!"


Add an element to the K list.
Code:
AddElement(K())
K() = "List element one"


Add an element to the M map.
Code:
M("one") = "Map element one"


Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Fri Apr 01, 2011 4:30 pm 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 6:14 pm
Posts: 1391
Location: Germany (Saxony, Deutscheinsiedel)
@spikey: I have added your latest contribution to the "Variables and processing of variables" chapter. Thanks a lot! :D

_________________
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)


Top
 Profile  
Reply with quote  
 Post subject: Decisions and Conditions Item - updated
PostPosted: Fri Apr 01, 2011 7:32 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Wed Sep 22, 2010 1:17 pm
Posts: 250
Location: United Kingdom
There are different ways of processing data obtained from user input or other way (loading from a file, ...). The common arithmetic functions (+, -, *, /, ...) can be combined with conditions. You can use the "If : Else/ElseIf : EndIf" set of keywords or the the "Select : Case/Default : EndSelect" keywords, just use what is the best for your situation!

This example shows the use of "If : ElseIf : Else : EndIf" creates a message, possibly for showing in the status bar of a form or something similar, based upon the number of items and filtered items in an, imaginary, list. Note that unlike some other BASIC languages, PureBasic doesn't use the "Then" keyword and that there is no space in the ElseIf and EndIf keywords.

Code:
Define.L Items = 10, Filter = 6
Define Message$

If Items = 0
  Message$ = "List is empty."
 
ElseIf Items = 1 And Filter = 0
  Message$ = "One item. Not shown by filter."
 
ElseIf Items > 1 And Filter = 0
  Message$ = StrU(Items) + " items. All filtered."
 
ElseIf Items > 1 And Filter = 1
  Message$ = StrU(Items) + " items. One shown by filter."
 
ElseIf Items = Filter
  Message$ = StrU(Items) + " items. None filtered."
 
Else
  ; None of the other conditions were met.
  Message$ = StrU(Items) + " items. " + StrU(Filter) +" shown by filter."
 
EndIf

Debug Message$



This example shows the use of Select : Case : Default : EndSelect to categorize the first 127 ASCII characters into groups. The "For : Next" loop counts to 130 to demonstrate the Default keyword.

Code:
Define.C Char
Define Message$

For Char = 0 To 130
 
  Select Char
     
    Case 0 To 8, 10 To 31, 127
      Message$ = StrU(Char) + " is a non-printing control code."
     
    Case 9
      Message$ = StrU(Char) + " is a tab."
     
    Case 32
      Message$ = StrU(Char) + " is a space."
     
    Case 36, 128   
      Message$ = StrU(Char) + " is a currency symbol. (" + Chr(Char) + ")"
     
    Case 33 To 35, 37 To 47, 58 To 64, 91 To 96
      Message$ = StrU(Char) + " is a punctuation mark or math symbol. (" + Chr(Char) + ")"
     
    Case 48 To 57
      Message$ = StrU(Char) + " is a numeral. (" + Chr(Char) + ")"
     
    Case 65 To 90
      Message$ = StrU(Char) + " is an upper case letter. (" + Chr(Char) + ")"
     
    Case 97 To 122
      Message$ = StrU(Char) + " is a lower case letter. (" + Chr(Char) + ")"
     
    Default
      ; If none of the preceding Case conditions are met.
      Message$ = "Sorry, I don't know what " + StrU(Char) + " is!"
     
  EndSelect
 
  Debug Message$
 
Next Char


Last edited by spikey on Wed Aug 03, 2011 5:56 pm, edited 3 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Sat Apr 02, 2011 7:34 pm 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 6:14 pm
Posts: 1391
Location: Germany (Saxony, Deutscheinsiedel)
Thanks spikey, I've added this to the "Decisions and Conditions" chapter. :D

_________________
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)


Top
 Profile  
Reply with quote  
 Post subject: Re: PureBasic Docs- Ideas/Help needed for a "We start" chapt
PostPosted: Sat Apr 02, 2011 11:57 pm 
Offline
PureBasic Expert
PureBasic Expert
User avatar

Joined: Sat May 17, 2003 11:31 am
Posts: 6032
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.

_________________
( PB5.50 Win7 x64 Dell XPS710 nForce 5 Intel Q6600 SSD Evo 840 GFX GTS450 )
( You have two options: psychotherapy, or the PureBasic Survival Guide... )


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 116 posts ]  Go to page 1, 2, 3, 4, 5 ... 8  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye