Creating Items in a Canvas

The whole point of having a Canvas is to put items in it. You can create arcs, bitmaps, images, lines, rectangles, ovals (circles), polygons, text, and widgets. Each has an associated createXXX method, where the type of item you want to create replaces the XXX. Each of the create methods returns a unique ID which can be used to refer to the item later. When you see a method that takes a tag or an ID as an argument, the ID is the one returned from the create method.

The Arc Item

When you create an arc, you specify a bounding rectangle with two sets of x and y coordinates. The arc is drawn within the confines of the bounding box. The basic createArc statement is as follows:

$id = $canvas->createArc(x1, y1, x2, y2);

Any additional options used with createArc are specified after the coordinates:

$id = $canvas->createArc(x1, y1, x2, y2, option => value);

Each option for the arc item can be used later with the itemcget and itemconfigure Canvas methods. The options are:

The Bitmap Item

A Canvas widget can display a bitmap instead of text, just as a Button or Label can. You can use createBitmap to insert a bitmap into your Canvas widget:

$id = $canvas->createBitmap(x, y);

Of course, you must use the -bitmap option to specify which bitmap to display or you won't see anything. So we really create a bitmap like this:

$id = $canvas->createBitmap(x, y, -bitmap => bitmap);

The other options available for createBitmap are:

The Image Item

If we can create a bitmap on a Canvas, it makes sense that we can create an image as well. We can do so with the createImage method:

$id = $canvas->createImage(x, y, -image => image);

Again, you have to specify an image to display or you won't see anything. The other options available for createImage are:

The Line Item

The createLine method can actually create multiple connected lines, not just one. The first two coordinate sets you supply create the first line, and any additional coordinates will continue the line to that point:

$id = $canvas->createLine(0,0, 400,400); # creates one line $id = $canvas->createLine(0,0, 400,400, -50, 240); # creates two lines

After the coordinates, you can specify any options and values you wish to configure the line(s); the options and values are as follows:

Specify the three distances by using an anonymous list such as this:

$canvas->createLine(10, 10, 200, -40, -arrow => "both", -arrowshape => [ 20, 20, 20]);

Figure 9-4 shows what the distance values mean. Figure 9-4

Figure 9-4. Definition of arrowhead

The Oval Item

An oval can be a circle if you draw it just right. To create a circle or oval, use the createOval method and specify two sets of points that indicate a rectangle (or square) in which to draw the oval. Here is a simple example:

$id = $canvas->createOval(0,0, 50, 50); # creates a circle $id = $canvas->createOval(0,0, 50, 100); # creates an oval

The options for the oval will be familiar, so we'll just cover them briefly:

The Polygon Item

A polygon is merely a bunch of lines where the first point is connected to the last point automatically to create an enclosed area. The createPolygon method requires at least three (x, y) coordinate pairs. For instance, the following piece of code will create a three-sided polygon:

$id = $canvas->createPolygon(1000,1000, 850,950, 30,40);

Additional (x, y) coordinate pairs can be specified as well; for example:

$id = $canvas->createPolygon(1000,1000, 850,950, 30,40, 500,500);

The options you can specify with createPolygon are the same as those you use with createLine: -fill, -outline, -smooth, -splinesteps, -stipple, -tags, and -width. Just remember that createPolygon connects the first point to the last point to enclose the area.

The Rectangle Item

As if being able to create a rectangle using createLine or createPolygon weren't enough, we also have the createRectangle method. It only takes two (x, y) coordinate sets, which are the opposite corners of the rectangular area:

$id = $canvas->createRectangle(10, 10, 50, 150);

Again, we have seen the options available for createRectangle with the other create methods: -fill, -outline, -stipple, -tags, and -width. Although we've covered these options already, here are a few examples:

# A blue rectangle with black outline: $canvas->createRectangle(10,10, 50, 150, -fill => 'blue'); # A blue rectangle with a thicker outline: $canvas->createRectangle(10,10, 50, 150, -fill => 'blue', -width => 10);

The Text Item

Finally, an item type that doesn't have lines in it! You can use the createText method to add text to a Canvas widget. It requires an (x, y) coordinate pair, which determines where you place the text in the Canvas and the text to be displayed:

$id = $canvas->createText(0,0, -text => "origin");

The -text option is actually optional, but then you wouldn't see any text on the screen. Because there is no point in that, we will assume that you will always specify -text with a text value to display. The other options available for text items are as follows:

The following options, discussed earlier, work the same as they would for an Entry widget or a Text widget: -insertbackground, -insertborderwidth, -insertofftime, -insertontime, -insertwidth, -selectbackground,-selectborderwidth, and-selectforeground. See "Label and Entry Widgets" and "The Text, TextUndo,and ROText Widgets" for more details.

Text item indexes

Methods that affect text items will sometimes ask for an index value. Text indexes for the regular Text widget were covered in "The Text, TextUndo,and ROText Widgets", and the index values for a Canvas text item are similar. The only difference is that each item is considered only one line (even if it has "\n" characters in it). Index values are as follows:

Deleting characters

To delete characters from within a text item, use the dchars method: $canvas->dchars(tag/id first [ last ]). Specify a tag or ID to match the text item(s) and the index at which to start deleting. If the end index isn't specified, all the characters to the end of the string will be deleted (including any "\n" characters).

Positioning the cursor

To specifically place the blinking text cursor, use the icursor method : $canvas->icursor(tag/id index). The cursor will only show up immediately if the specified item has the current keyboard focus. You can still set the position of the cursor if it doesn't, it just won't display until the item does get the keyboard focus.

Index information

To find an index based on another index, use the index method. Here's an example:

$index = $canvas->index("textitem", "sel.first");

This returns the numerical index associated with the first selected character in the text item. If more than one item matches the tag or ID indicated (in this case, it's a tag named "textitem"), then the first one found is used.

Adding text

To add more text to a text item, use the insert method: $canvas->insert(tag/id index string). The first argument is the tag or ID, which can match multiple items. The second argument is the index before which to insert the new string, and the last argument is the actual string to insert into the text item.

Selecting text

There are several methods you can use to programmatically select portions of the text. To clear the selection (any selection; there are no tags or IDs sent with this command), use $canvas->selectClear. To select a portion of text, use selectFrom and selectTo. The following two lines of code select the text from beginning to end for the first item that matches the tag "texttag":

$canvas->selectFrom("texttag", 0); $canvas->selectTo("texttag", "end");

You can use the selectAdjust method to add to the selection: $canvas->selectAdjust("adjust", tag/id index). To get the ID of the item that currently has the selection in it, use $id = $canvas->selectItem.

The Widget Item

You can put any type of widget inside a Canvas-Buttons, Checkbuttons, Text widgets, or even another Canvas widget (if you are a little crazy)-by using the createWindow method. Before calling createWindow, you must create the widget to put into the Canvas. Here's an example:

$bttn = $canvas->Button(-text => "Button", -command => sub {
 print "Button in Canvas\n";
}
); $id = $canvas->createWindow(0, 0, -window => $bttn);

There are a few things you should note about this example (which is fairly typical, except the subroutine associated with the Button doesn't do anything useful):

The following options, which you can use when you call createWindow, are more like options you use with pack than widget options:

It makes sense to create the widget inline if you don't need to do anything fancy with it.

The Grid Item

Perl/Tk has an experimental grid item type that displays dotted, dashed, or solid lines, in both the x and y dimensions. The effect is reminiscent of old-fashioned graph paper.

Grid items cover the entire Canvas, but never enclose or overlap any area and are not near any point, so you cannot search for them using the closest, enclosed, or overlapping attributes. In most other regards, they behave like other Canvas item types. Currently, grids, like window items, do not appear in PostScript output.

Grid items are created like this:

$canvas->createGrid(x1, y1, x2, y2, ... );

x1 and y1 specify the origin of the basal grid cell, and x2 and y2 specify its width and height, respectively; the cell is replicated over the entire surface of the Canvas widget. By default, dots are drawn at every grid intersection, unless the -lines option is set true. When drawing lines, dash specifications are honored.

This code generated Figure 9-5:

my $c = $mw->Canvas(qw/-width 300 -height 200/)->grid; $c->configure("-scrollregion" => [0,0, 300, 200]); $c->createGrid(0, 0, 10, 10); $c->createGrid(0, 0, 50, 50, -lines => 1, -dash => '-.'); $c->createGrid(0, 0, 100, 100, -width => 3, -lines => 1);

One important note: grid items remain invisible unless a scroll region is defined. This may be construed as a bug, which is one reason why grids are deemed experimental. Figure 9-5

Figure 9-5. A canvas with three grid items

The Group Item

Perl/Tk sports a new Canvas item called a group. A group item is actually a collection of standard Canvas items that can be manipulated simultaneously. The following code creates an oval and a rectangle, then groups them together:

$one = $canvas->createOval(5, 0, 20, 30, -fill => 'blue'); $two = $canvas->createRectangle(0, 20, 50, 75, -fill => 'red'); $group = $canvas->createGroup([0, 0], -members => [$one, $two]); $mw->update; $mw->after(1000); $canvas->move($group, 100, 100);

Figure 9-6 shows the outcome. Figure 9-6

Figure 9-6. Before move

After a one-second delay, the group is moved to a new Canvas coordinate, shown in Figure 9-7. Figure 9-7

Figure 9-7. After move

Of course, sometimes you want to get to individual items within the group, perhaps to configure a special attribute. The current idiom is to iterate through the members of the group, like this:

foreach my $member ($canvas->itemcget($group, -members)) {
 print "member=$member\n";
}

This example prints out the item ID for each member of the group, but you can use the item IDs in an itemconfigure call.