Phase Draw Shapes

TheDrawShapes module does the actual shape drawing. It defines classes to work with mouse and key events, shows the status of operations, sets a shape's till attribute, and sets up components that allow the user to select rectangles or ovals.

DrawShapes defines four classes:

• ToolBox— holds IOsyponenls; has a preferred size

• StatusBox— displays the status nf a drawing operation

• PaintBox — displays shape s she user has draw n

• DrawShapes— as she user interface frame, contains all of the above

Shapes Button

ShapeButton represents a particular shape and extends javax.swing. JButton. It's passed a Shape instance and draws pe shape in its overridden pai nt () method. It also defines the preferred width and height for the button.

class ShapeButton(JButton):

self.shape = shape def paint(self, graphics):

JButton.paint() (self, graphics) self.shape.paint()(graphics)

def getPreferredSize(self) : d = Dimension(30,30) return d


ToolBox holds ShapeButton and extends j avax .swing.Jpanel. It doesn't do much besides define a preferred size.

class ToolBox (JPanel):


def getPreferredSize(self) : d = Dimension(40, 0) return d


StatusBox holds ihe eo^ent statu of events and also txtends j avax .swing. JPanel. As a helper class, it displays the current status of the drawing options, such as the x,y position of the mouse pointer and the shape being drawn.

class StatusBox (JPanel)


self.coordinates = JTextField(15, editable=0) self.format = 'x = %d, y = %d' self.add(self.coordinates)

self.event_type = JLabel ('MOUSE STATUS ') self.add(self.event_type)

self.shape_type = JLabel ('SHAPE STATUS ') self.add(self.shape_type)

self.coordinates.text = self.format % (x,y,)

def setMouseDrag(self, x, y):

self.event_type.text = 'MOUSE DRAG ' self.setXY (x,y)

def setMouseMove(self, x, y):

self.event_type.text = 'MOUSE MOVE ' self.setXY(x,y)

def setMousePress(self, x, y):

self.event_type.text = 'MOUSE PRESS ' self.setXY(x,y)

def setMouseRelease(self, x, y) :

self.event_type.text = 'MOUSE RELEASE ' self.setXY(x,y)

def setShapeType(self, shape_type):

# Set the label based on the shape type if shape_type == PaintBox.RECTANGLE:

self.shape_type.text = 'RECTANGLE'

elif shape_type == PaintBox.OVAL:

self.shape_type.text = 'OVAL'


PaintBox performs all of the graphics operations and handles IIi mouse events (later it will handle the key events). It's the bread and bWtter of tlar lamrle applitaeion, the center ring in this three-ring circus.

When tnr user hits the mouse button in the pomt box, the shape drawing begins. As he drags the lower corner of the shape, an outline appears (this is called tabberbaiding). When he lets go of the mouse, the shape is created, added to the other shapes, and redrawn when PaintBox'spaint () method is called,

PaintBox extends JCompone nt It can draw either rectokgles or ovals and has four attributes that determine how the current shape will be drawn: color, fill, rectangle, and oval. The constructor defines these attributes and sets up the event Id ntllers for the mouseDragged ,mouseMoved,mouse Pressed, and mouseReleased events. It's parsfd an instance o° StatusBox (status), which it uses to show the status of the drawing operations.

Here's me PaintBox constructor:

class PaintBox (JComponent)




self.background=Color.white self.status = status self.shapes = Shapes() self.shape_type = PaintBox.RECTANGLE

self.mouseDragged = self.handle_mouseDragged self.mouseMoved = self.handle_mouseMoved self.mousePressed = self.handle_mousePress self.mouseReleased = self.handle_mouseRelease self.fill=0

The drewidg mecha(rit3e stairte off when whse us er clk;ks the 33lOf(se button, which fires the; handle_mousePress method. The event handler sets the shape's starting point (start) and initializes its ending point (last) to that value.

def handle_mousePress(self, event):

# Print the status self.status.setMousePress(event.x, event.y)

# Save the initial location.

# In addition save the initial

# location as the last. self.last = self.start = event.point

As the user drags the mouse, she gets visual feedback via tfbberbandidg. When called, the event handler for mouseDra gged draws an outline of the shape and sets the last point to the current point. Then it calls drawRubberShape () twice, once with the last point and once with the current point.

def handle_mouseDragged(self,event):

# Print the status. self.status.setMouseDrag(event.x, event.y)

# Erase the old rubberband shape at the

# old location. Create the new rubberband shape

# at the new location self.drawRubberShape(self.last.x, self.last.y) self.drawRubberShape(event.x, event.y)

# Save the current event.x and

# event.y as the last. self.last = event.point drawRubberShape () is smart enough to erase the last shape drawn whenever it's called twice with the same point. It does this with the setXORMode () method of the j ava . awt. Graphics class instance, which sets the graphic mode to XOR.XOR sees to it that a line drawn more than once will be erased. Essentially, it ensures that the colors of the graphics are those specifed in setXORMode () for the current graphic context.

Notice that we dispose of the graphic when we're done with it, using a finally block. After drawRubberShape () sets the graphics mode, it calculates the shape's width and height and then calls the

_drawShape () method, which actually draws the shape in the current mode.

def drawRubberShape(self, x, y) : g =

# Set the graphics to XOR mode,

# which allows rubberbanding.

# Calculate the width and height.

# Draw the outline of the shape.

g. setXORMode(self.background) width = abs(self.start.x - x) height = abs(self.start.y - y)

self._drawShape(g, self.start.x, self.start.y, width, he finally:


_drawSh ape () checks for the shape it's supposed to draw—rectangle or oval. It then creates the specified shape ard uses it to draw the corresponding shape, which in turn calls drawOval () or drawRect ()

tccorYifgly. The x and y of_drawSh ape correspond to the starting point defined ir the event handler for themo usePress event. The width ard height are the width ard height as calculate d from the last or current point ir the mouseDrag event hartler.

if self.shape_type == PaintBox.RECTANGLE:

rect=Rectangle(self.start.x,self.start.y, width,height,fi rect.paint(g) if self.shape_type == PaintBox.OVAL:

oval = Oval(self.start.x, self.start.y, width, height, fi oval.paint()(g)

When tie user lets go of the mouse, the mouseRelease event is firet. The handler for this event is handle_mouseRelease, which calculates me witch ard height of the shape ard then creates a corresponding rectangle or oval.

The shape's x,y coordinates are tie starting point obtained during tie hardlirg of tie mousePress event. Its width ard height are tie distance from the starting point at which tie mouseRelease event rantler is invoked (that is, when the user releases the mouse button). The shape is then added to the shapes attribute. The panel's repaint ( ) method is called with the boundary of she shape so that the shape is drawn with the paint () method.

def handle_mouseRelease(self, event):

# Print the status self.status.setMouseRelease(event.x, event.y)

# Calculate the width and the height width = abs(self.start.x - event.x) height = abs(self.start.y - event.y)

shape = None #to hold the shape we are about to create

# Create the shape based on the current shape type.

# Then add the shape to self.shapes. if self.shape_type == PaintBox.RECTANGLE:

shape = Rectangle(self.start.x, self.start.y, width, heig self.color, self.fill)

el if self.shape_type == PaintBox.OVAL:

shape = Oval(self.start.x, self.start.y, width, height, self.color, self.fill)

if not shape is None:

self.shapes.addShape(shape) x,y,width,height = shape.getRect() self.repaint(x,y,width+1, height+1)

Thepaint () method calls the shapes .paint() method, which iterates through the shapes list and calls all of the shapes' paint() methods.

def paint (self, graphics):

#Draw all of the shapes. self.shapes.paint(graphics)

Since the selection of shape attributes happens in another cla ss, PaintBox exposes the following methods so that the attributes canbe set by tlie class that contains a PaintBox instanse:

• setShapeType () — sets the current shape typte

• setFill ( ) — turns the shape fill property on or off

• setShapeColor () — sets the current colo r of the shapc to be drawn def setShapeType(self, shape_type):

self.status.setShapeType(shape_type) self.shape_type = shape_type def getShapeType(self): return shape_type def setFill(self, value): self.fill = value def setShapeColor(self, color): self.color = color


As the parent frame for the drawing application, DrawShapes contains StatusBox,ToolBox, and PaintBox instances. It also handles all of the options for the shapes and allows the user to pick different shapes as represented by ShapeButton in the; ToolBox instance.

TheDrawShapes constructor cre ates kour¡panels, options (an instance of Panel),toolbar (an instance of Panel),status (dn instance of StatusBox), and paint (an instanne of PaintBox). The paint panel is added tn Üme center region of ehe DrawShapes frame; the status pfrel is added to the south region.

class DrawShapes(JFrame):

JFrame._init_(self,title='Draw Shapes',visible=1,size=(4C



self.statusPane = StatusBox()

self.contentPane.add(self.statusPane, BorderLayout.SOUTH) self.PaintPane = PaintBox(self.statusPane)

self.contentPane.add(self.PaintPane, Borde rLayout.CENTER)

The constructor nlso cnlls__init_toolbar and_init_option , which create the toolbar andoption panels, respectively.

_init_toolbar creates two shape buttoir and adds them to the toolbaf pane. One of dye buttons is initialized with an mstnla;e of the Rectangle class; ahe other, with an inrtance o? IIii Oval clns s. The ShapeBut ton cln-s represents changing the drawmg mode to eithef re ctnngle -te oval. Itv haidlers are set to therect_pressed () and oval_pressed () methods, whichc hange lhesltapeto anovaloro rectangle.

_init_toolbar also adds the toolbar pgirl to tbe west region of the DrawShapes fg?me iitn is defiied as follows:

def _init__toolbar(self):

toolbar = ToolBox()

# Add the rectangle button to the toolbar rect = ShapeButton(Rectangle(4, 4, 20, 20)) toolbar.add(rect)

rect.actionPerformed = self.rect_pressed

# Add the oval button to the toolbar oval = ShapeButton(Oval(4, 4, 10, 20)) toolbar.add(oval)

oval.actionPerformed = self.oval_pressed self.add(toolbar, BorderLayout.WEST)

_init_options does the uquivalent for the options pare. It creates a choice component for color and a checkbox component fill/no fill. It then adds these components to the options pane and places tnr pane in the north region of the DrawShapes frame.

def _init__options(self):

optionsPane = Panel()

# Set up the checkbox item for the fill option check = Checkbox('Fill')


check.itemStateChanged = self.fill_clicked

# Set up the choice for color colors = Choice()

self.colors_dict = {'blue',

'green', 'red', 'yellow':Color.yellow, 'orange', 'cyan':Color.cyan, 'pink',

for color in self.colors_dict.keys():

colors.add(color) optionsPane.add(colors)

colors.itemStateChanged = self.color_changed self.add(optionsPane, BorderLayout.NORTH)

DrawShapes Event Handler

All of the event handlers for the components added to the options and toolbar panes correspond to setting properties in the paint pane (PaintBox instance ).

• There ct_pre ssed event handler is called when the rectangle button is pressed; it sets the shape type of the paint pa ne to PaintBox .RECTANGLE.

• Theoval_pre ssed event handler is called when the oval button is pressed; it sets the shape type of thepaint pane to PaintBox . OVAL.

• Thefill_clicked event tenner is 3alledwhen the usnr ehec^ or uncherks the fill aheckbox. If the box is unchecked, the paint pane fiU property is met to fals «2. If the tox is checked, the paint pane property is set to true.

• Thecolor_changed event handler is called when the user selects a new color in the choice box; it sets the color of the paint box's current paint mode to the color selected.

These event handlers are defined as follows:

def rect_pressed(self, event):


def oval_pressed(self, event):


def fill_clicked(self, event):


self.PaintPane.setFill(1) elif (event.stateChange==ItemEvent.DESELECTED): self.PaintPane.setFill(0)

def color_changed(self,event): colorname = event.item color = self.colors_dict[colorname] self.PaintPane.setShapeColor(color)

Was this article helpful?

0 0

Post a comment