WINDOWS PROGRAMMING WITH BASIC
GFA Basic simplifies Windows programming while maintaining DOS compatibility
Raymond J. Schneider
Ray is the director of engineering at ComSonics Inc. in Harrisonburg, Va. He is a licensed professional engineer in the state of Virginia and a doctoral student of Information Technology at George Mason University.
Writing programs for Windows 3 can be a daunting task, especially with Microsoft's SDK (System Development Kit) and other C-based development environments. Basic, however, promises to deliver Windows programming with much less effort. And with Microsoft's Visual Basic, Within Technologies' Realizer Basic, and GFA Software's GFA Basic for Windows, there's suddenly a plethora of Windows development tools available for all programmers.
Sure, many programmers think that "real programmers" don't use Basic, but it can't be beat for writing quick, scientific programs. (In the IEEE 488 world of instrument control, in fact, Basic is the norm.) Basic is also terrific for checking algorithms and building those quick-look prototypes.
Consequently, when I received a copy of GFA Basic for Windows 3, I was anxious to put it through its paces. To test GFA Basic's effectiveness as a Windows programming tool, I built an application based on a GW Basic program I'd previously published (see Computer User, November 1990) that uses Lorenz equations to show how mathematical chaos works. The Windows version of the application presented here uses standard Windows features such as menuing, multiple windows, buttons, and so on.
Programming for Chaos
In his efforts to understand the unpredictability of weather, MIT meteorologist Edward Lorenz discovered chaos when experimenting with a simplified model of the weather in the early '60s. He found that even tiny changes in the starting conditions quickly led to major shifts in the predicted behavior. This came to be called the "Butterfly Effect," because changes as small as the flick of a butterfly's wing in initial conditions led to substantial changes later, in the time evolution of the equations. Lorenz concluded that if weather actually behaves in a fashion similar to his equations, then long-range weather prediction isn't possible.
The Lorenz equations, shown in Example 1(a), are a simplified model of atmospheric convective warming -- a flat, fluid layer is warmed from below and cooled from above. The variable x represents the convective motion, y the horizontal temperature variation, and z the vertical temperature variation. Sigma, Rho, and B are proportional to the Prandtl number, the Rayleigh number, and the size of the region being approximated, respectively. All these parameters are positive.
Example 1: (a) The Lorenz equation; (b) a typical loop DO loop to process events (UNTIL MENU signals that the close box has been moused); (c) using a SWITCH statement to process menu selections.
(a) dx/dt=SIGMA*(-x+y) dy/dt=-x*z+RHO*x-y dz/dt=x*y-B*z (b) DO GETEVENT [Statements to Process Events] UNTIL MENU(1)=4 (c) IF MENU(1)=20 SWITCH MENU(0) CASE 1 ... ENDSWITCH ENDIF
The Lorenz equations are three dimensional, so you can have three different normalized plane views. Consequently, I wanted to use a menu bar to select views of the Lorenz equations to graph, and to provide some pop-up requesters to set the parameters. To keep it simple, I wanted to have a menu bar that let me select views (XY, YZ, XZ), a pop-up requester to set the initial parameters (Sigma, Rho, and B), and finally a Go menu item to start the calculations and plotting. The equations run in all the selected boxes until a predefined number of points has been plotted; selecting Go again will plot another slug of points.
My first step was getting the menu bar to activate. This seemed easy because all you have to do is initialize a string array with the various menu titles, submenu titles, and separating blank strings and then invoke the menu with a MENU array$() command.
All communications between GFA Basic and Windows take place through an array named MENU(), which is only a little confusing. You have to get used to Windows doing all sorts of things over which you have little control: At any moment your program must be prepared to respond to an event caused by some operator action. (This event-driven programming style does not come naturally to a Basic programmer used to strictly procedural code.)
Processing Events
GFA Basic hooks into the Windows environment through a variety of functions, the MENU() array, ON MENU functions, and looping while listening to Windows. A typical construct is shown in Example 1(b).
There are two ways to process menu items: The first uses a SWITCH statement in the event loop, and the second uses the ON MENU GOSUB statement. In the application (see listing One, page 112), I chose the first method, which nests a SWITCH statement inside an IF statement.
In Example 1(c), for instance, MENU (1) is set to 20 to signal a menu-bar event, and the string array selected is contained in MENU(O). It took me a little time to discover that the menu titles, despite being highlighted and having a string-array number, were not returned in MENU(O). So, I changed my menu bar to have GO as a submenu item rather than a main-menu item. There's probably a way to detect the mouse click on the main-menu item, but I didn't find it in the documentation. Once you can invoke a menu-bar item, it's simple to incorporate the necessary instructions in the CASE elements of the SWITCH statement.
Dialogs and Views
Next I needed a dialog box to pop up on the screen and let me enter the critical parameters of the Lorenz equations -- SIGMA, RHO, and B. GFA Basic supplies a lot of push buttons, radio buttons, check boxes, combo boxes, list boxes, and so on. But all I wanted was a simple box to enter data into and a button to push when finished. Digging through the documentation produced instructions on how to set up a dialog box. The procedure SetDialog() records the process. Within a DIALOG statement, you list the properties and locations of the control elements that you want in the dialog box. When you've defined all these items, you put the dialog box on the window with a SHOW-DIALOG #n statement, where #n is the number of the dialog.
The views were established as child windows -- windows inside a parent window. To maximize simplicity, I didn't try to incorporate resizing or redrawing functions, but with enough attention to detail (all those messages in the MENU() array) you can detect window resizing, requests to paint the window over, and all the other amazing amount of stuff you have to keep track of to maintain a well-behaved Windows application.
The HandleMessage() and GetText() procedures were modeled on similar procedures in the GFA Basic manuals.
Computing the Function
The final task was to add the computation and graphics to the application. I wrote a single procedure, PlotLorenz (X,Y,Z,N%,I%), which plots N% points starting at X,Y,Z in the plane identified by the integer I% (1 = XY, 2 = YZ, 3 = ZX). On repeated presses of GO, the program continues to plot points in additional groups of N%, set to 100 in the program. The actual plotting was accomplished using the LINE x1,y1,x2,y2 statement. I made no attempt to scale the data for proper aspect ratio or "handedness," although that would be more accurate for visualization.
Nevertheless, the resulting program is interesting. First you pop open the windows you want (usually all three), then open the control menu and select "Set Parameters." Next, you enter the values in the EDITTEXT boxes and mouse on the OK button to accept the values. Finally, click the mouse on GO and continue until satisfied. Restart the program for another run. A reasonable starting value is SIGMA = 60, RHO = 40, B = 20. This gets you a view of a strange attractor with a lazy-eight orbit; see Figure 1.
About GFA Basic for Windows
While the chaos program gave me an opportunity to sample the joys of Windows programming, I've only lightly touched the power of GFA Basic. The programming environment combines a syntax-sensitive editor and a direct-mode environment which I didn't find much occasion to use. The editor is easy to use and helpful, because it refuses to let you enter a line with incorrect syntax. It could, however, be confused by incorrectly terminated compound statements (NEXT I% or ENDIF, for example).
The language itself is rich in capability. It includes a large array of graphics functions, linear algebra (matrix) functions, and a host of specialized functions for handling the Windows environment. Remarkably, except for about 25 Windows-specific commands, the language is compatible with its DOS counterpart, so that developers creating applications in GFA Basic can easily transport them, complete with windowing functions, to a non-Windows environment.
GFA Basic for Windows comes with several example programs, written in GFA Basic, which double as utilities and examples of powerful Windows code. RCS.GFW is an elementary dialog editor which generates ASCII text files of statements that create the code for radio buttons, combo boxes, and so on for an application. When you SAVE your window with the various elements that you have selected and positioned, you get the necessary GFA Basic instructions written to a text file, which can then be merged with your program under development.
ICOEDIT.GFW is another example, this time of an icon editor. I played around with ICOEDIT and created a little icon with a feather pen and an ink bottle. Then I saved the icon to a file with an .ICO extension.
To test the built-in compiler which generates .EXE files, I first asked it to make an .EXE file out of the ICO-EDIT.GFW. This it did in a flash. I attached the ink-pot icon I'd designed to the file and popped it into my program-manager window. I double-clicked on the ink pot and there I was, running the icon editor as an independent .EXE file. The compiler ran quickly and transparently.
I found several of the example programs somewhat fragile. Some locked up my machine, forcing an ALT-CTRL-DEL to escape from the situation.
GFA vs. Visual Basic
How does GFA Basic stack up to the more famous Microsoft Visual Basic? The two are sharply different in philosophy and feel. Visual Basic is a dramatic departure from traditional procedural programming languages because it requires the programmer to adopt a particular event-driven, state-machine model of programming.
Visual Basic programs are not composed in a text-editor environment, but rather in an environment that resembles a draw program. The left side of the screen contains a vertical panel of tool icons, while in the center of the screen is a generic window to receive the programmer's ministrations.
To construct a Visual Basic program, you first create the forms that will communicate with the user. You then add features such as menus, text boxes, and buttons to your forms. Most of this is accomplished by "hook and drag" mousing around. Then you add code to each element of your form or control in a text editor, so that when the control is invoked, the program does the right thing. After saving your code as a project, you test it and create an executable. This is easy for small programs with just a few choices, but it gets complex fast.
The tools Visual Basic provides for manipulating forms include a variety of buttons and boxes as well as labels, scrolling controls, and timers. If you don't find enough controls already built in, Microsoft offers the Control Development System so you can develop your own.
For small programs, Visual Basic is the hands-down winner in the ease-of-use category. As programs get larger, however, you'll find that more discipline is required in using Visual Basic, because the event-driven paradigm fragments the program into small pieces which may be invoked under a variety of complex conditions. Debugging such a beast is rather different from debugging structured procedural code. Moreover, when you print out your Visual Basic programs, you don't get all the code. Menus generated in the menu generator, for example, don't produce any printout when you print out your program. The same is true of your forms, which are built up but not documented in a readable form by the system.
GFA Basic is harder to use for small programs than Visual Basic, but it has the advantage of offering source-code compatibility with its DOS counterpart to create windows and pull-down menus, and to control mouse actions. In addition, GFA Basic adds advanced graphics functions to create splines, ellipses, arcs, and Bezier curves; DLLs to access and update dBase files; LAN support; support for huge arrays up to 20 Mbytes; and more.
Conclusion
GFA Basic is a good way to get into Windows programming and offers a way of more quickly creating at least relatively simple Windows applications. A good friend of mine likes to talk about the law of "Conservation of Complexity" -- there's no such thing as a free lunch. While GFA Basic can help make Windows programming a little less difficult, the fact remains that there's a complex beast under those windows, and it takes a complex program to keep up.
Products Mentioned
GFA Basic for Windows 3 GFA Software Technologies Inc. 27 Congress St. Salem, MA 01970 508-744-0201 $295
_WINDOWS PROGRAMMING WITH BASIC_ by Raymond J. Schneider[LISTING ONE]
<a name="00ef_000e"> /* Lorenz Equations in GFA BASIC /* Copyright 1991 Raymond J. Schneider XEND=0,YEND=0,ZEND=0 //Initializes end of plot run variables /* Configure and Open Parent Window pstyle%= WS_OVERLAPPEDWINDOW OR pstyle%, WS_CLIPCHILDREN OR pstyle%, WS_VISIBLE TITLEW #1, "Lorenz Equations" PARENTW #1,0,0,_X,_Y,pstyle% //Open full window /* Configure three child windows cstyle%=0 cstyle%= WS_SYSMENU /* Title the windows TITLEW #2, "Lorenz XY-View" TITLEW #3, "Lorenz YZ-View" TITLEW #4, "Lorenz ZX-View" /* Initalize Menu Structure DIM m$(15) m$(0)="&Views" //First Menu Item m$(1)="&XY-View" // Sub-Menu Items m$(2)="&YZ-View" m$(3)="&ZX-View" m$(4)="" // Null-string terminates sub-menus m$(5)="&Control" m$(6)="&Set Parameters" m$(7)="&GO" m$(8)="" m$(9)="" MENU m$( ) /* Main Program cww%=_X/2-30,chh%=_Y/2-30 ON MENU MESSAGE GOSUB HandleMessage( ) DO GETEVENT IF MENU(1)=20 //Then a Menu Selection Has been Pressed /* Process Menu Selection SWITCH MENU(0) CASE 1 // Open XY-View CHILDW #2,1,5,5,cww%,chh%,cstyle% CASE 2 // Open YZ-View CHILDW #3,1,cww%+8,5,cww%,chh%,cstyle% CASE 3 // Open ZX-View CHILDW #4,1,5,chh%+8,cww%,chh%,cstyle% CASE 6 //Set-Parameters EXIT IF DLGRV&<>0 SetDialog() DO SLEEP UNTIL DLGRV& TEXT 0,0,"Sigma="+STR$(SIGMA) TEXT 0,12,"Rho= "+STR$(RHO) TEXT 0,24,"B= "+STR$(B) CASE 7 // Calculate Lorenz and Plot in Windows TOPW #2 IF XEND=0 //Initialization of starting conditions X,Y,Z X=1 ELSE X=XEND ENDIF IF YEND=0 Y=1 ELSE Y=YEND ENDIF IF ZEND=0 Z=1 ELSE Z=ZEND ENDIF PlotLorenz(X,Y,Z,100,1) TOPW #3 PlotLorenz(X,Y,Z,100,2) TOPW #4 PlotLorenz(X,Y,Z,100,3) ENDSWITCH ENDIF IF MENU(1)=4 SWITCH MENU(14) CASE 2 CLOSEW #2 CASE 3 CLOSEW #3 CASE 4 CLOSEW #4 ENDSWITCH ENDIF EXIT IF MENU(1)=4 && MENU(14)=1 LOOP CLOSEW #1 END PROCEDURE HandleMessage( ) IF DLG(1)=MENU(15) ENDIF /*Discard General Messages DLGRV&=0 hw%=GetParent(MENU(15)) IF hw%=DLG(1) SWITCH MENU(6) CASE 100 IF MENU(1)=30 GetText(1,101,SIGMA$) GetText(1,102,RHO$) GetText(1,103,B$) SIGMA=VAL(SIGMA$),RHO=VAL(RHO$),B=VAL(B$) DLGRV&=MENU(6) ENDIF ENDSWITCH ENDIF RETURN PROCEDURE GetText(d|,di&,VAR a$) LOCAL buffer$ buffer$=SPACE$(100) IF GetWindowText(DLGITEM(d|,di&),V:buffer$,LEN(buffer$))>0 a$=CHAR{V:buffer$} ELSE a$="" ENDIF RETURN PROCEDURE SetDialog() s%=WS_TABSTOP sb%=s% sb%|=BS_DEFPUSHBUTTON seb%=s% seb%|=WS_BORDER seb%|=ES_UPPERCASE ~SetFocus(DLGITEM(1,100)) DLGRV&=0 DIALOG #1,325,175,300,160,"Set Parameters" DLGBASE UNIT DEFPUSHBUTTON "OK",100,80,65,14,12,sb% EDITTEXT "",101,80,10,40,12,seb% EDITTEXT "",102,80,30,40,12,seb% EDITTEXT "",103,80,50,40,12,seb% RTEXT "SIGMA",104,10,10,55,12 RTEXT "RHO",105,10,30,55,12 RTEXT "B",106,10,50,55,12 ENDDIALOG SHOWDIALOG #1 RETURN PROCEDURE PlotLorenz(X,Y,Z,N%,I%) /* SIGMA, RHO, B assumed Global LOCAL LX,LY,LZ,LX1,LY1,LZ1,DT,j% LOCAL DX,DY,DZ PX=_X/4+50,PY=_Y/4+10 // Plot Offsets LX=X,LY=Y,LZ=Z,DT=.01 FOR j%=1 TO N% DX=SIGMA*(LY-LX)*DT DY=(-LX*LZ+RHO*LX-LY)*DT DZ=(LX*LY - B*LZ)*DT LX1=LX+DX LY1=LY+DY LZ1=LZ+DZ SWITCH I% CASE 1 //PLOT XY LINE LX+PX,LY+PY,LX1+PX,LY1+PY CASE 2 //PLOT YZ LINE LY+PX,LZ+PY,LY1+PX,LZ1+PY CASE 3 //PLOT ZX LINE LZ+PX,LX+PY,LZ1+PX,LX1+PY ENDSWITCH LX=LX1,LY=LY1,LZ=LZ1 // Update function NEXT j% XEND=LX,YEND=LY,ZEND=LZ RETURN
Copyright © 1992, Dr. Dobb's Journal