| |||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||
Print This Article REALbasic University: Column 085
OOP University: Part NineToday we're going to take a little break from tedious OOP lecturing and create a simple drawing program. I'll bet that sounds intimidating. But because we're doing this with object-oriented programming, you're going to surprised how easy this is. It took me a mere twenty minutes to create this from scratch!
SimpleDrawOur goal with SimpleDraw is to show the power of overriding, a concept we introduced in our last lesson. To keep things simple, our program will support just three kinds of objects: circles, rectangles, and triangles. (We can always add more later.) We begin by adding four classes to our project ("New class" from the File menu). Rename these like this: ![]() ShapeClass will be our generic parent class. For each of the others, make them a subclass of shapeClass. You do this by setting their super to ShapeClass on the Properties palette: ![]() Do that for circleClass, rectClass, and triangleClass. Now double-click on shapeClass. Add the following method and properties (don't forget that draw has a g as graphics parameter): ![]() Easy, eh? Now the cool thing is that since shapeClass has these items, so do the others by inheritance! However, shapeClass, while it has a draw method, doesn't really do anything. After all, what's a shape? That's too vague to draw. But we can do specific drawings for each of the defined shapes, however. So open each subclass and add a draw method. Here's the method for circleClass:
The method for rectClass is almost identical:
Here's the method for triangleClass, which is a little more complicated:
Guess what? We're almost done! We just need to create an interface for our program and give us a way to manipulate our objects. First, let's create the interface by dragging objects from the Controls palette. Try to make it look something like this: ![]() There are three pushButtons on the left, a separator in the middle, and the box on the right is a canvas -- that will be our drawing area. Now there's one very important step we must do. The pushButtons should be a control array. Starting the with top one, make them all have the same name, addObjectButton. When you get to the second one, REALbasic should ask you if you want to make this a control array: say yes. When you're finished, the index (on the Property palette under the name) of each pushButton should be 0 for the first one, 1 for the second, and 2 for the third. If not, change the indexes to match these numbers (important). Good. We're now ready to add code to window1 and create our drawing program. First, let's add a property to window1. This will be our data structure: it will hold a list of all the objects we add. Open window1 and add objectList(0) as shapeClass as a property ("New Property" from the Edit menu). Next, go to the Controls section and find the Paint event of canvas1. Put in this code:
Believe it or not, these few lines draw all our objects! That's because all we do is loop through our object list and tell each object to draw itself. Marvelously simple! But of course our drawing routine does nothing unless there are objects to draw. We need to add a way to generate objects.
Adding ObjectsThis is the most complicated part of SimpleDraw. But you'll see it's not complicated at all! First, we allocate more room in our objectList array. This will be for the new object we'll be adding. Next, we generate some random sizes and locations for the object. I did it this way for simplicity, but of course you could add a slider control and let the user set the size of the new object that way (or any other way you desire). Once we've got the size and location determined, we simply create a new instance of the object kind we're creating (circle, rectangle, or triangle). Remember, addObjectButton is a control array -- this one set of code will create all three of our object types. Since we're passed the index of the control array, we can test for each kind of object (0 = circle, 1 = rectangle, 2 = triangle). Just in case you've messed up creating your control array (i.e. added an extra button), we create a shapeClass object if index doesn't match any of our other cases. Because shapeClass doesn't do anything, adding an instance of shapeClass won't hurt anything (though it still exists as an object in memory). Our final step is to simply assign the size and location values to the object. Since all our objects have the same properties, this code is the same for all the objects. Here's the code for the Action event of addObjectButton:
Isn't that simple? The final line simply tells canvas1 to redraw itself, and since we've just added a new object to objectList, that new object is immediately drawn. The effect to the user is we just added a circle, rectangle, or triangle to our drawing area! Running the program and clicking the buttons generates wonderful artwork that looks something like this: ![]() Obviously, this isn't a full-fledged drawing program... yet. It's surprisingly close, however. If we added ways to select, move, and resize objects, we'd be darn close. Guess what? With a few changes to our program, we can easily add that behavior! Don't believe me? Tune in next time when we soup up SimpleDraw into SuperDraw! If you would like the complete REALbasic project file for this week's tutorial (including resources), you may download it here. Next WeekWith a few simple modifications we upgrade SimpleDraw into SuperDraw!
News![]() Good news! Those of you who've been following the progress of REALbasic Developer magazine know that we've been waiting for months to receive our periodicals mailing permit so we could send magazines outside of the United States at the much less expensive periodicals rate. Well, the U.S. Postal Service has finally granted our application! This means we're finally able to ship out magazines to all overseas subscribers. Now normally when you subscribe to RBD your subscription starts with whatever's the current issue at that time. But since we haven't mailed any issues to international subscribers, we're going to start all international subscriptions with the first issue. Even better, if you're a new international subscriber and you subscribe before we do our initial mailing early next week (Feb. 18th, 2002), we'll give you the same deal! This means you can get all the previously published issues without paying the back issue price. This offer applies only to non-U.S. print subscriptions, and you must subscribe by Feb. 17th, 2003. Take advantage of this once-in-a-lifetime offer! In case you missed it last week, here's a preview of what's inside the February/March issue. Marketing Your Software Designing for Aqua Interview with TouchCAD Creator Postmortem Much More
LettersThis week we hear from Joe Rice, who has a problem with the getFolderItem function. He writes:
First, I don't find anything obviously wrong with your scripts. However, you are forgetting to check for one kind of error, and I bet that's what's happening. Both your textInputStream and textOutputStream objects have the possibility of being nil if the file couldn't be opened or created. I suspect that's the nilObjectError you're getting. To fix this, simply insert in an if fileReadFrom <> nil then statement after the .openAsTextFile line. (Do the same with the fileStream object in the other script.) As to why this error would occur, there are several possibilities, but the most probable is that REALbasic can't find the file to open it. Why can't it find it? Well, you aren't giving it much to go on: you're just telling it to open "preferences.text" (with no path to it). Since you aren't giving it a specific, repeatable location, REALbasic looks for it in its default locations. Here it gets tricky, especially with an older version of REALbasic. When you're in the IDE, REALbasic's default path is to look within the REALbasic folder. But when you have a compiled application, REALbasic looks inside the folder where that compiled app resides. A far superior way to do this is to put this file in your Preferences folder (I'm assuming since its name is "preferences.text" its a preference file for your application). Don't know where the Preferences folder is? No problem: RB will tell you! Just change your getFolderitem code to this: file = preferencesFolder.child("preferences.text")
What this does is use the preferencesFolder function to return a folderitem pointing to your Preferences folder, wherever it may be, and regardless of your operating system. Then you add the child method, passing it the name of the file you're seeking: that will find a file by the name you pass that resides inside the parent folder (the user's Preferences folder). By doing it this way you're guaranteed this code will always work. You still always need to check that the file exists, of course -- the first time an app runs the prefs file probably isn't there. I'd also recommend you change the name of your file from a generic "preferences.text" to something unique that includes the name of your program since it will now be in a more public location (with lots of other prefs files). Hope this solves your problem! P.S. I sent this to Joe earlier and he wrote me back to say it indeed did fix things for him. 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.
| |||||||||||||||||||||||||||