| |||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||||
Print This Article REALbasic University: Column 016
Two Sample In-House ProgramsLast week I described several REALbasic programs I created for personal use. Some of those are fairly complicated -- too complex for me to explain how they work in a few paragraphs (I don't want to dwell excessively on these minor programs, just give you a taste of RB's capabilities). I do have a couple very simple programs that I thought illustrate the power and convenience of REALbasic. We'll go over these in detail in this week's lesson.
Fast FingerA year or so ago I discovered ABC's Who Wants to Be A Millionaire and watched it regularly for a while. (I finally gave it up as I just don't have time, but I still like the show.) I even attempted to qualify as a contestant. Unfortunately, while my trivia skills are above average, my speed skills are not. Give me ten seconds to put four ordinary household appliances in alphabetical order and you'll get a blank expression. I liked playing along with the show at home, but the "Fastest Finger" contest always annoyed me: it goes so fast I have trouble measuring my own time, and by the time I've figured out the third or fourth part of my answer I've forgotten what I picked for the first part! Of course it's unfair: the contestants have buttons they can push to pick to set the order of their answers while I have to remember everything while keeping an eye on my stopwatch. So one night -- during the show -- I whipped open REALbasic and created Fast Finger. This is a dirt simple program designed to do just one thing: record how fast you can push four buttons. That's it. If you run it while you're watching the show, you can test yourself against the other contestants during the "Fastest Finger" portion of the show. It's not fancy and it's nearly useless, but it gave me something to do during the commercials. It also demonstrates a few nice things about REALbasic. And if you're lucky, it might even help you get on the game show! Since the user interface of Fast Finger is a little complicated, I'm going to give it to you all finished. That way all you have to do is put in a few lines of code (which I'll explain), and you'll be all set to test your finger speed. First download the following REALbasic project (you'll need Stuffit Expander to decompress it): Open the project in REALbasic (it's in RB 2.1 format, so it will work with REALbasic 2.1 or 3.x). This is what Window1 looks like in the IDE. ![]() The project does nothing at this point: it's just an interface, with no code. First, let's add an important property to the main window, Window1. We're adding it to Window1 because we want all routines within Window1 to be able to access this property -- to be able to look at it or change it. Go to the Edit menu and choose "New Property" and type in "startTime as double" (no quotes) in the dialog box that is displayed. Click Okay or press return. ![]() The first code we'll add is to the Timer1 object. A timer is a special REALbasic object that executes after a certain amount of time has elapsed. You set the amount of time, in milliseconds, with the timer's period property. In this case I've preset Timer1 to 10 milliseconds: that means one hundred times per second, whatever code is in Timer1's Action handler will be executed. Select Timer1 on Window1 (open the window if it is closed) and press Option-Tab (hold the Option key down while pressing the Tab key). That will open the Code Editor right to Timer1's Action event. Now put in the following (copy and paste it, if you want): Source code for Window1.Timer1.Action: dim theTime, secs, huns as double What the above does is display a clock of the current time. We use REALbasic's microseconds function to find out the current time to 1000th of a second. Then we break that information into two pieces, the number of seconds since the countdown was started (startTime) and the number of hundredths of a second since the countdown was started. Finally, we display the time in a staticText object. Now let's take a look at our pushButtons. You may have noticed that while we have four visible buttons on Window1, there is only one Button in the Controls list of the Code Editor: ![]() Shouldn't there be four buttons listed? No, there shouldn't. That's because I've used a special REALbasic feature: a control array. Remember when we learned about arrays while builing GenderChanger? Well, a control array is an array that contains controls. In this case, pushButtons! Just like with a regular array, you access individual objects within the array using an index number. (Notice that in the Control list Button() has parenthesis at the end: that's a reminder that it's a control array.) (Note that the Button() control array's index starts at zero, so the buttons are number 0-3, not 1-4.) The benefit of a control array is enormous: instead of having four similar yet separate buttons, each with identical code, I can have one button with a single set of code! I can always tell which button the user has clicked by checking the Index variable that REALbasic sends with all events.
In this case, we're just going to pass the button pushed to a method (which we'll write in a second) which will handle the push. All we have to put into the button's Action handler is this (don't include the bold title, of course): Source code for Window1.Button.Action: doButton(index) Next, let's go to Window1's Open method. All we're going to do here is make sure that all four of our pushButtons are disabled when the program is first launched. Since our buttons are a control array, this is simple. Source code for Window1.Open: dim i as integer Now go to Window1's Keydown event. This method gets called whenever the user presses a key. What we want to do here is mimic buttons being pushed with key presses. In other words, the user can click button "A", type "A", or type "1" and all those actions have the same result. To do this we simply use a select case statement (see RB's help for more details) and if the user's typed an appropriate letter or number, we send a command to our "doButton" routine. Source code for Window1.KeyDown: select case uppercase(key) So what does this magical "doButton" routine do? Well, let's find out! Create a new Method by going to the Edit menu and choosing "New Method". For the method name, use "doButton" (no quotes). For the method's parameters, put "index as integer" (that will allow this routine to receive the button number). Leave the return value empty, and click Okay. ![]() In the method's code area, type (or paste in) the following: Source code for Window1.doButton: if button(index).enabled then Now go to startButton's Action method and type in "startEverything" (no quotes). This is going to be a separate routine that initializes the program (resets the clock, turns on all the buttons, etc.). Create a new Method called "startEverything" with no parameters or return values. Here's the startEverything code: Source code for Window1.startEverything: dim i as integer As you can see, it's simple. Guess what? We're done! That's the whole program. Run it and you should be able to start the timer, click the buttons, and see what order you clicked them and how long it took, just like on TV. Here's what the program looks like in action: ![]() If you don't feel like typing in all that code, here's the complete program, ready to compile and run.
Count EmailsThe second program I wanted to demonstrate is one I wrote one day while studying my Stone Table Software customer list. I was looking at all the emails -- many from foreign countries -- and I started wondering what percentage of my customers come from which countries. Email addresses and domains aren't a surefire way to determine a person's origin, but they can tell you a little. So I sat down and wrote this little program which accepts a text file of emails (one email per line) and sorts and counts them by extension (.com, .net, etc.). To start, create a new project in REALbasic. Using the default Window1 that's created for you, drag a listbox onto it. Make the listbox big so it fills all of Window1. ![]() For ListBox1's properties, check the HasHeading option and put "3" for the number of columns. For columnWidths, put "50%, 25%, 25%". (That will divide ListBox1 into three columns, the first at a width of 50% and the others at 25%.) Your properties window should look like this: ![]() Option-Tab with ListBox1 selected to open the Code Editor. Go to ListBox1's Open event. Here we're going to put in the following: Source code for Window1.ListBox1.Open: me.acceptFileDrop("text")
The above does two things: one, it sets ListBox1 to allow text files to be dropped on it, and two, it names the listbox's headers. While we're thinking about it, let's make sure our project has a "text" File Type created. Go to the Edit menu and choose "File Types". If you see an item there named "text", you're finished. If not, you'll need to add one. (Without the File Type being defined, REALbasic wouldn't know what kinds of files to allow the user to drop on the listbox.) Click the "Add" button, name it "text" and use "ttxt" for the Creator and "text" for the Type. Click Okay twice and you're finished. Now go to the DropObject event. This is a major part of the program. What happens is the user drops a text file on the control, so first we check to make sure they dropped a file and then we open it. We load up theList array with each line from the text file, then close it. We call a few other routines -- stripList, sortIt, countDuplicates, and updateList -- and then we're done. Source code for Window1.ListBox1.DropObject: dim in as textInputStream We're going to need to create all those methods now, so let's get started. None of the methods have any parameters or return any values, so they're simple. I find it easier to create them all first, then go back and fill in the details later. So go to the Edit menu and choose "New Method" a bunch of times and use the following names for the methods:
When you've got all three methods ready, let's put in the code, starting with stripList. It's a routine that deletes everything but the email extension from the email addresses. The algorithm used is a simple one: it simply looks for the last phrase deliminated by a period. Since all email address end in a dot-something, the text to the right of the dot is saved and everything else is thrown away (stripped). This way our list of emails is paired down to simple "com", "net", and other extensions. If other words, this:
becomes
Source code for Window1.stripList: dim n, i as integer
CountDuplicates is a little more complicated. It loops through the entire list and counts how many of each kind exist. If it finds a duplicate, it does two things: it deletes the duplicate from theList and increments the number stored in that item's count. (The item's count is stored in the itemCount array.) Note how we don't use a for loop for the outermost loop; I used a while loop instead. I did this because the size of the loop changes: when we find a duplicate item we delete it, reducing the number of items in the list. Using a while loop means that the computer will keep repeating through the list until it reaches the last unique item. Source code for Window1.countDuplicates: dim total, n, i, j, found as integer The final part of CountDuplicates is where we calculate the percentage of each kind of email. To do that we first need a total: not the number of elements in the list, but the total number of emails. So we count through the itemCount array, adding up each item. Since an itemCount element contains a zero if there's only one of that item, we add a one to it to get the actual count. We finish by initializing the percentList array to the size of our list, then set each element in the array to the appropriate percentage. Note that we again add one to the itemCount value. Our final method for the program is the updateList method. This is used to display the information in listBox1. It first deletes any existing rows in listBox1 (if you didn't do that, dropping a second file on the existing listbox would display the wrong content), then counts through the list of items. For each item in the list, it puts the content in the appropriate column of the listbox. The first item is in the addRow command: that's the ".com" or whatever was part of the email address. Once we've added a row, we don't want to add another, we want to work with the row we just added. So we use the lastIndex property which contains the index number of the last row we added. We use the cell method to put the actual item count (again, we add one to it) and the percentage into the correct columns. Source code for Window1.updateList: dim n, i as integer Whew! Our program's almost done: we just need to add our arrays as properties to Window1. So with the Window1 code editor window visible, go to the Edit menu and choose "New Property". You'll do this three times, adding the following properties:
Save your program and run it. It should display a simple window with a listbox in it. Oh no! You need some email addresses to test this, don't you. Okay, here's a list of hopefully fictitious addresses I made up. (I tried to use a variety of extensions.) Save them to a text file and you'll be able to drag the text file to the listbox. (Option-click on this link to save the text file to your hard drive.)
After you drag the file to the listbox, your window should look similar to this: ![]() Once again, for those who don't feel like typing in all that code, here's the complete program, ready to compile and run. Good. That's it for this week. I hope these little programs were helpful and you learned something. For me they represent the real power of REALbasic: the ability to quickly create a solution to a little problem without spending any money. Next Week: We'll try a little animation by using sprites to create a simple two-dimensional arcade game.
LettersFor those of you who responded to last week's survey on things you'd like to do with REALbasic, thanks! I received many responses and I'll be publishing a summary in a future column. If you haven't sent in your response, feel free to send it in now. Our first question this week comes from Tim & Sarah Reichelt, who write:
Interesting question! Odd that I've never ran into this, but there doesn't seem to be a way to do what you want. REALbasic provides no method for setting the color of either individual lines or the entire contents of a listbox. If this is something you really need, bug REAL Software and get them to add the feature. In the meantime, there are a couple potential workarounds, but like all workarounds, they have limitations and disadvantages. First, you could check on the web to see if someone has created a replacement listbox which lets you change the text color. There are plug-ins to REALbasic which allow to create more sophisticated controls, and there are RB-made classes which mimic standard controls. (A good place to start is the RB web-ring linked at the top of this page.) Another approach, if your listbox needs are modest, is to create your own listbox class. It's a bit more complicated than I can detail here, but if you don't need listbox features like multiple columns, editable text, folders, or pictures, it wouldn't be too hard to roll your own using a canvas and a scrollbar control. Remember, a canvas is simply a graphics area: you can draw lines of text in it, each in a different color if you want, and use a scrollbar control to scroll the visible text. (If that sounds like it would work for you and you don't find an existing solution on 'net, let me know: I may be interested in creating this as an example project for RBU.) I also received a letter from Sir Steven, who includes a slew of questions in a single paragraph:
Learn to use the return key, Sir Steven! Just teasing. The answers to your questions, as best as I can, are as follows. How to create a global variable? Put the variable as a property inside a module. Modules are inherently global and any property put in a module is available to all parts of your program. Use a method? A method is REALbasic's equivalent of a procedure/function/subroutine in other languages. (By the way, if you put a method in a module, it too is global.) Do you need to fill in all the edfields? I'm not sure what you mean by this; are you referring to the fields on the Properties palette? If so, the answer is no: REALbasic will use default (standard) values if you don't override them, so you only need to change the ones you find necessary. How do you change its value? Once you've created a global variable, you can change its value simply by assigning it a new value (as in "gVar = 10"). Since it's a global, you can do this anywhere in your program. Are there any other ways to make global variables? No, that's pretty much it. If you define a property that's part of an object (such as a window), the variable is local only to that window. Of course you can still access it from another object by naming it directly, so it's not inaccessible, just awkward. For instance, if Window1 contains a property called myVar, then a routine in Window2 can access it by saying Window1.myVar. Keep in mind that there are degrees of globalness: a property local to Window1 is essentially global for Window1 (all routines inside Window1 can access the variable). That might be all you need. A variable created inside a particular method, however, is only available within that method: other routines cannot access it. Therefore a true global variable must be created inside a module so that all routines can access it. I hope that helps; if I misunderstood one of your questions, let me know and I'll try to remedy it next time.
New RBU FeatureNote that at the top of this and all past RBU columns, we now have a special "Print this article" link. This will take you a print-format version of the column. If you prefer to have a local version on your hard drive, you can either use your web browser's "Save as web archive" feature, or use Jim Walker's PrintToPDF driver and print the page as a PDF file. About the Column REALbasic University is a weekly instructional column on programming with REALbasic and is brought to you by REALbasic Developer, the magazine for REALbasic programmers. Each week we answer select reader questions, and we're always open to ideas for future columns. Send your questions to . (Keep your questions simple and specific. General queries like "How do I write my own web browser?" will be neglected.) Your question won't be answered immediately, but will be answered in a future column. (If you don't want your correspondence published, just be sure to indicate that when you write. Otherwise it's fair game.) About the Author See the REALbasic University Archives
REALbasic University contents ©2001-2004 by Marc Zeedar and REALbasic Developer. All Rights Reserved.
| |||||||||||||||||||||||||||||||