This page describes Calico Graphics, a 2D, Object-Oriented graphics library for creating art, games, visualizations, and animations in any of the Calico languages.
See also Calico Differences which lists the differences between CPython Myro and IDLE.
You will find a set of examples at http://svn.cs.brynmawr.edu/viewvc/Calico/trunk/examples/ in various languages.
- 1 Importing Graphics
- 2 Windows
- 3 Shapes
- 3.1 Shape Properties
- 3.2 Shape Methods
- 3.3 Gradient
- 3.4 Arrow
- 3.5 Dot
- 3.6 Curve
- 3.7 SpeechBubble
- 3.8 Text
- 3.9 Line
- 3.10 Picture
- 3.11 Rectangle
- 3.12 RoundedRectangle
- 3.13 Polygon
- 3.14 Circle
- 3.15 Oval
- 3.16 Pie
- 3.17 Arc
- 3.18 Sprite
- 3.19 GUI Widgets
- 3.20 Groups of Shapes
- 4 AnimatedGifInterface
- 5 Window Features
- 6 Low Level Graphics
- 7 Plot
The Calico Graphics library allows for the creation of graphical objects in a window. The library Calico Myro contains just a few of the Graphics functions:
- makePicture(...) (for Picture())
The full Graphics module is available for many Calico languages, including Python, Ruby, F#, Java, and Scheme. For Jigsaw, you might be more interested in using Calico Shapes, a functional interface to Calico Graphics.
Importing in Languages
To import Calico Graphics in each language requires to use the syntax from each specific language.
python> import Graphics
ruby> require "Graphics"
scheme> (using "Graphics")
fsharp> #r "Graphics.dll";;
The rest of this page describes the Graphics library using Python syntax.
For example, you can use:
from Myro import *
to include the subset of Graphics functions and classes contained in the Calico Myro module. To directly load all of the defined functions from the Graphics library in Calico, one could:
from Graphics import *
This will make all of the functions and classes available directly:
circle = Circle((100,200), 20)
Or, you could also:
However, then you must always use the "Graphics." prefix on functions and classes:
circle = Graphics.Circle((100,200), 20)
You can also just import the items you want, like so:
from Graphics import Circle
The following code assumes that you have imported all of the functions and classes from Graphics. The term "shape" is used to describe generic graphical objects.
Shapes can be drawn on a Window. You must create a window before you can see any shape:
win = Window("My Title", width, height) win = Window("Title of Window") win = Window(width, height) win = Window()
Window can take three optional arguments: title, width, and height. The defaults are "Calico Graphics" and 300 width by 300 height. For example:
win2 = Window("My Second Window") # creates a window with title "My Second Window" that is 300 x 300 win3 = Window("My Third Window", 400) # creates a window with title "My Third Window" that is 400 x 300 win4 = Window("My Fourth Window", 400, 600) # creates a window with title "My Fourth Window" that is 400 x 600 win5 = Window(500, 500) #creates an anonymous window that is 500 x 500
Coordinates for Calico graphics have (0,0) as the top left-hand corner, with the horizontal direction increasing as it goes to the right, and the vertical direction increasing as it goes down.
You can always recall the last window created using the getWindow() function:
Window("New Window") win = getWindow() # gets last created window win = getWindow("New Window") # gets window by title
Note that Graphics will use reuse windows based on the title. For example:
win1 = Window() win2 = Window()
will only create one window, and both win1 and win2 will refer to it. To create two windows, use different titles:
win1 = Window("Window 1") win2 = Window("Window 2")
You can also make windows using the following functions:
- makeWindow (title, width, height)
- makeWindowFast (title, width, height) # made to reuse quickly
- makeWindow (title, widget)
Adds scrollbars on a window and expand the underlying canvas to width x height.
Calico Windows operate in one of four modes: "auto", "manual", "bitmap", "bitmapmanual", or "physics". If in "auto", "bitmap", or "physics" mode, then changes to the window are made as soon as possible so that you can see their effect. However, if too many updates are made, then you will only see the changes occur every win.updateInterval seconds. By default, win.updateInterval is .1 seconds (100 milliseconds).
The "bitmap" and "bitmapmanual" modes will not render shapes as shapes, which are redrawn each time them window needs to be re-rendered, but draws them once to a bitmap area associated with the window. This makes redrawing very fast, but at the cost of being unable to treat shapes as objects. For example, you would not be able to "move" a shape if it were drawn in bitmap mode.
The "manual" and "bitmapmanual" modes are designed so that you can control how fast the window updates, and makes sure that all of the objects update together. You must use the win.step() (or win.step(SECONDS)) method when you have the win.mode set to "manual" or "bitmapmanual". The window will not update faster than SECONDS. By default, SECONDS is 0, so it will update as soon as you call step().
If you want the system to update automatically (the default), use:
win.mode = "auto" # this line is not needed, as this is the default win.setMode("auto") # another method to set the mode (same as above) win.updateInterval = .1 # this line is optional
That will update the window no more than every .1 seconds. This helps make your computer more efficient, so that it only makes updates every so often. You wouldn't want the computer to update the screen for every pixel change or else your graphics would slow to a crawl. If you want to force an update in this mode, use:
If you want to control exactly when a window updates, then use:
win.mode = "manual" ... win.step(.1) # or win.step()
This will force the update when you call step(.1), but will make sure that it doesn't update faster than every 100 ms. That number allows you to speed up and slow down animations.
Setting mode to "auto" is good for interactive use, but "manual" and using step() is good for making animations.
win.mode = "bitmap" Line((0, 0), (100, 100)).draw(win)
Here the Line is created, and drawn to the bitmap of the window. There is no use saving the Line object. You can switch back and fourth between bitmap and manual/auto. That will allow you to have some objects, and some drawings.
win.getMode() # returns the mode win.mode # returns the mode
The "physics" mode is discussed below.
There are three main functions for dealing with mouse events:
- getMouse() - waits until user clicks and returns (x, y) of location in window
- getMouseNow() - gets (x, y) of mouse location, relative to window
- getMouseState() - gets current mouse state ("up" or "down")
You can also attach functions to mouse events so that the function will define what to do.
You can attach as many functions as you like. Each function takes the window and the event:
def handleMouseUp(obj, event): print("Up") def handleMouseDown(obj, event): print("Down") def handleMouseMovement(obj, event): print("Movement") win = Window() onMouseMovement(handleMouseMovement) onMouseUp(handleMouseUp) onMouseDown(handleMouseDown)
All of these mouse functions are also methods of the Window class. So if you have multiple windows, you can call the function directly:
win = Window() win.getMouse() win.getMouseNow() win.getMouseState() win.onMouseMovement(handleMouseMovement) win.onMouseUp(handleMouseUp) win.onMouseDown(handleMouseDown)
The obj argument is (in these cases) a Graphics.WindowClass instance, and the event is an Event object:
- event.x - x component of event
- event.y - y component of event
- event.time - time of event, in global seconds
- event.type - returns name of event type: "mouse-release", "mouse-press", "mouse-motion", "key-press", "key-release"
- event.key - returns string key name from a key event
And for key events:
- event.type - "mouse-motion", "mouse-press", "mouse-release", "key-press", "key-release"
- event.key - key name of press or release (e.g., "Return", "A", "Tab", etc.)
Other events have various obj and event properties:
- event.type = "click"; obj is Button - for Button widget
- event.type = "change-value", event.value is 0 to 101; obj is Slider - for HSlider and VSlider
See also the joystick(), getGamePad() and getGamepadNow() functions of the Calico Myro library.
See also the new Calico Events module for event-driven programming.
To use the key handling functions, you need to have a window created.
There are two main functions for dealing with keyboard events:
- getKeyPressed() - gets a string of the last key pressed
- getKeyState() - gets the state of the keyboard (a key "up" or a key "down")
Using these functions, you might miss a key press because you only have access to the last key press and key state from the last Graphics.Window created.
To handle every key press from any Window, as with handling every mouse action, you should attach functions to keyboard activity:
These functions, like the mouse event handlers, are passed the window and the event:
def handleKeyPress(win, event): print("Down") def handleKeyRelease(win, event): print("Up") win = Window() onKeyPress(handleKeyPress) onKeyRelease(handleKeyRelease)
The KeyPress event will be called continuously while the key is down.
The win argument is a Graphics.WindowClass instance, and the event is a Event object (see above).
See also the new Calico Events module for event-driven programming.
New in Calico 3.0.0
To use the key handling functions, you need to have a window created.
There are three main functions for dealing with keyboard events:
- getKeyPressed() - returns True if any key is currently pressed, False otherwise.
- getKeyPressed(key) - returns True if key is currently being pressed, False otherwise.
- getLastKey() - returns the last key that was pressed. (This method does not consume the event upon reading.)
You can create and draw a variety of shapes in a window. Each shape results in a representation on a window when drawn. However, it is also an object which can be moved around, changed in various ways, and even undrawn.
Shapes require Points to define their boundaries or center. Wherever a Point is required, you can optionally use a list or tuple.
All shapes have the following properties:
- border - thickness of border of shape
- center - center of shape
- center.x - x coordinate
- center.y - x coordinate
- color - shortcut for setting both fill and outline (need to set to a color first)
- fill - color of area of shape
- gradient - (as opposed to fill) - a Gradient() of colors (see below)
- outline - color of border of shape
- pen - leaves trail if penDown()
- points - points that make up a shape (if needed)
- rotation - direction of rotation
- scaleFactor - amount of scaling
- tag - string for labeling item
- window - the (last) window a shape is drawn in
In addition, a shape can have Physics properties, if it is drawn on a window when the window is in "physics" mode:
- body - represents the Physical object
- bodyType - either "static" or "dynamic"
- bounce - 1.0 is 100% bounce; 0.0 is no bounce
- density - set this before you draw it
- friction - amount of friction
- mass - set this after you draw it (with win.mode set to "physics")
- wrap - set to True to keep shape on the screen
All shapes (which includes anything that you would draw on a window) have the following methods:
- shape.draw(window) - put shape on window's list of items to draw
- shape1.draw(shape2) - draw shape1 into shape2's reference frame
- shape.drawAt(window, position) - put shape on window's list of items to draw
- shape1.drawAt(shape2, position) - draw shape1 into shape2's reference frame
- shape.forward(distance) - move shape in its zero rotate direction
- shape.getP1() - returns first point of a shape in screen coordinates
- shape.getP2() - returns second point of a shape in screen coordinates
- shape.getScreenPoint(p) - given a point relative to the center of a shape, returns a point in global screen coordinates
- shape.getX() - get the x component of the center of a shape
- shape.getY() - get the y component of the center of a shape
- shape.move(dx, dy) - move by delta dx, delta dy
- shape.moveTo(x, y) - move to x, y
- shape.penDown() - put the shapes pen down to allow trace when it moves
- shape.penUp() - put the pen up and stop trace.
- line = shape.penUp(True) - put the pen up and stop trace, erase trace, and return line
- shape.setPenColor(color) - short for:
- line = shape.penUp(True)
- line.color = shape.pen.color
- line.drawAt(window, position)
- shape.pen.color = color
- shape.rotate(degrees) - rotate by degrees (positive is counter-clockwise)
- shape.rotateTo(degrees) - rotate to degrees (0 is to the right, moving positive clockwise)
- shape.scale(factor) - scale by a percent (.1 is 10% of original; 1.1 is 10% larger)
- shape.scaleTo(factor) - set scale to factor (1 is 100% of original)
- shape.setX(x) - set the x component of the center of a shape
- shape.setY(y) - set the y component of the center of a shape
- shape.speak(text) - a speech bubble will appear next to shape
A Gradient is composed of two colors and a transition between them.
- Gradient("linear", point1, color1, point2, color2)
- Gradient("radial", point1, radius1, color1, point2, radius2, color2)
from Graphics import * win = Window() sq = Rectangle((100, 100), (200, 200)) sq.gradient = Gradient("linear", (-50, 0), Color("red"), (50, 0), Color("blue")) sq.draw(win) c = Circle((250, 250), 50) c.gradient = Gradient("radial", (0, 0), 10, Color("red"), (0, 0), 30, Color(0, 0, 0, 0)) c.outline = None c.draw(win)
The rest of this section enumerates all of the specific types of shapes.
- Arrow((x, y), degrees) - create an arrow at (x,y) facing degrees (0 is to right)
- Arrow(Point(x, y), degrees) - create an arrow at (x,y) facing degrees (0 is to right)
An arrow is a triangular pointer. Often used as the shape for creating "turtle graphics".
# Turtle Graphics Example for Calico Python # After http://en.wikipedia.org/wiki/File:Turtle-Graphics_Polyspiral.svg # Doug Blank <firstname.lastname@example.org> from Graphics import * size = 600 win = Window("Turtle Graphics", size, size) turtle = Arrow((size/2, size/2), 0) turtle.draw(win) turtle.penDown() def f(dist, angle, incr, segs): for i in range(segs): turtle.forward(dist * (size * .35)) turtle.rotate(-angle) dist += incr f(.01, 89.5, .01, 184)
from Graphics import * win = Window() def spiral(x, y, d, loops, color): arrow = Arrow((150, 150)) arrow.pen.color = color arrow.draw(win) arrow.moveTo(x, y) arrow.rotateTo(d) arrow.penDown() i = 0.0 while i < .180 * loops: arrow.rotate(-2) arrow.forward(.1 + i) i = i + .001 spiral(150, 150, 0, 18, Color("black")) spiral(148, 158, 180, 18, Color("red"))
You cannot draw a Point; use Dot instead.
- Dot((x,y)) - draw a point at (x,y)
- Dot(Point(x,y)) - draw a point at (x,y)
from Graphics import * import random win = Window("Shapes", 200, 200) for i in range(5000): shape = Dot(random.random() * 200, random.random() * 200) shape.draw(win)
P1 and P4 are the end points; P2 and P3 are control points.
- Curve((x1, y1), (x2, y2), (x3, y3), (x4, y4))
- Curve(Point(x1, y1), Point(x2, y2), Point(x3, y3), Point(x4, y4))
from Graphics import * win = Window("Shapes", 200, 200) curve = Curve((10, 10), (50, 150), (150, 50), (190, 190)) curve.border = 3 curve.draw(win)
Draws a speech bubble on the window.
- SpeechBubble((x1,y1), (x2,y2), text, (x3,y3)))
- (x1,y1) is the upper-left hand corner
- (x2,y2) is the lower-right hand corner
- text is the words in the bubble
- (x3,y3) is the anchor point (origin of speech)
The SpeechBubble object also uses all of the text attributes (below).
from Graphics import * win = Window(300, 300) sb = SpeechBubble((10, 10), (200, 200), "Hello, world!", (100, 250)) sb.draw(win)
Place text on the window.
- Text((x, y), text)
- Text(Point(x, y), text)
- text.fontFace - typeface of font
- text.fontWeight - bold?
- text.fontSlant - italics?
- text.fontSize - size of font
- text.xJustification - "center" (or "left" or "right")
- text.yJustification - "center" (or "top" or "bottom")
- text.width - (read-only) width of text in pixels
- text.height - (read-only) height of text in pixels
from Graphics import * win = Window("Shapes", 200, 200) shape = Text((100, 100), "Hello, World!") shape.fill = Color("blue") shape.rotate(45) shape.draw(win)
Draw a line.
- Line((x1, y1), (x2, y2))
- Line(Point(x1, y1), Point(x2, y2))
- line = Line()
from Graphics import * win = Window("Shapes", 200, 200) shape = Line((10, 10), (190, 190)) shape.fill = Color("blue") shape.border = 3 shape.draw(win)
Create a picture.
- Picture(filename) - filename is a string including path
- Picture(window) - make a picture of the contents of a window
- Picture(URL) - get a picture from the web
- Picture(width, height) - make a blank picture width by height
- Picture(width, height, color) - make a blank picture width by height of a particular color
- Picture(picture) - makes a copy
- savePicture(filename, picture) - saves image in GIF, JPG or PNG format
- savePicture("filename.gif", picture, ...) - saves an animated GIF
- savePicture("filename.gif", [picture, ...]) - saves an animated GIF
- savePicture("filename.gif", delay, [picture, ...]) - saves an animated GIF
- savePicture("filename.gif", delay, loop?, [picture, ...]) - saves an animated GIF
- setTransparent(picture, color) - sets all pixels that match color to be transparent
- copyPicture(picture) - returns a copy of a picture
- Can also use makePicture(...) for all versions of Picture(...)
See also Animated Gif Interface
- alpha - change the alpha of all pixels
- width - width of image
- height - height of image
from Graphics import * win = Window("Shapes", 200, 200) shape = Picture(190, 190, Color("lightblue")) shape.draw(win)
from Graphics import * shape = Picture("http://4.bp.blogspot.com/__Y5vVtebEEE/SIeNV78jvAI/AAAAAAAABBY/9FIVWXy_Kho/s400/BarackObamaHead.jpg") win = Window("Shapes", shape.width, shape.height) shape.draw(win)
- getRegion(center_point, width, height, degrees) - get a region of an image
- setRegion(center_point, width, height, degrees, color) - set a region of an image to color
- setAlpha(byte) - set alpha for entire picture
- flipHorizontal() - flip the image horizontally around a vertical axis
- flipVertical() - flip the image vertically around a horizontal axis
There is support for moving an Image from Calico Processing to Calico Graphics, and back:
from Myro import makePicture from Processing import createImage p = makePicture(100, 100) i = createImage(p.toBitmap()) # turn Graphics.Picture into a Bitmap and load in Processing i.loadPixels() # load the Pixels p2 = makePicture(i.toBitmap()) # turn Processing.Image into a Bitmap and load in Graphics
Pixels are generally not created outside of the context of a Picture.
Properties of pixels:
- x - (read-only) x location in picture from whence it came
- y - (read-only) y location in picture from whence it came
- picture - (read-only) reference to the picture from whence it came
Global Pixel Functions:
- getPixel(picture, x, y)
- setPixels(picture1, picture2)
- setPixel(picture, x, y, pixel)
- setPixel(picture, x, y, color)
Pixels don't have color themselves, but you can get the color components:
- setColor(pixel, color)
- setRed(pixel, integer)
- setGreen(pixel, integer)
- setBlue(pixel, integer)
- setAlpha(pixel, integer)
What is the difference between Color and Pixel?
- Colors are independent
- Colors know their red, green, blue, and alpha
- Pixels depend on a particular Picture
- Pixels known their x, y, and associated picture
You can set the color of a pixel in a picture with:
- setPixel(pic, x, y, pixel)
- setPixel(pic, x, y, color)
Colors are composed of 4 components: red, green, blue, and alpha. All values are integers between 0 (dark) and 255 (bright). Alpha is the transparency of the color. An alpha of 255 is completely opaque, and an alpha of 0 is completely transparent.
- Color(r, g, b)
- makeColor(r, g, b)
- red - (read/write) integer value of red component (0 - 255)
- green - (read/write) integer value of green component (0 - 255)
- blue - (read/write) integer value of blue component (0 - 255)
- alpha - (read/write) integer value of alpha component (0 - 255)
Color is used:
- for each Pixel of a Picture
- for the color of a Picture
- fill of any shape
- outline of any shape
See also the function pickAColor() from the Myro library.
Draw a rectangle or square.
- Rectangle((x1, y1), (x2, y2))
- Rectangle(Point(x1, y1), Point(x2, y2))
from Graphics import * win = Window("Shapes", 200, 200) shape = Rectangle((10, 10), (190, 190)) shape.fill = Color("lightblue") shape.draw(win)
Draw a rounded rectangle or square. New in Calico 1.0.4.
- RoundedRectangle((x1, y1), (x2, y2), radius)
- RoundedRectangle(Point(x1, y1), Point(x2, y2), radius)
from Graphics import * win = Window("Shapes", 200, 200) shape = RoundedRectangle((10, 10), (190, 190), 10) shape.fill = Color("lightblue") shape.draw(win)
Draw a polygon by listing its points.
- Polygon((x1, y1), ...)
- Polygon(Point(x1, y1), ...)
from Graphics import * win = Window("Shapes", 200, 200) shape = Polygon((10, 10), (190, 10), (100, 190)) shape.fill = Color("lightblue") shape.draw(win)
Draw a circle.
- Circle((x, y), radius)
- Circle(Point(x, y), radius)
from Graphics import * win = Window("Shapes", 200, 200) shape = Circle((100, 100), 90) shape.fill = Color("lightblue") shape.draw(win)
Draw an oval.
- Oval((x, y), xradius, yradius)
- Oval(Point(x, y), xradius, yradius)
xradius is the distance (the width) when initially drawn; yradius is the distance (the height) when initially drawn.
# Pulse Example # Indicator that the computer is busy # Doug Blank <email@example.com> from Graphics import * from Myro import wait import math win = Window() alphas = list(reversed([x/10 * 255 for x in range(10)])) ovals =  for i in range(0, 360, 36): oval = Oval((150, 150), 50, 20) oval.rotate(-i) oval.color = Color("purple") position = int(abs(oval.rotation/(2 * math.pi) * 10)) oval.color.alpha = alphas[9 - position] oval.draw(win) oval.forward(60) ovals.append(oval) alphas.append(alphas.pop(0)) while True: for oval in ovals: position = int(abs(oval.rotation/(2 * math.pi) * 10)) oval.color.alpha = alphas[9 - position] win.step(.0075) alphas.append(alphas.pop(0))
Draw a slice of pie.
- Pie((x, y), radius, startDegree, stopDegree) - zero to right
- Pie(Point(x, y), radius, startDegree, stopDegree) - zero to right
The following example draws a Pacman-like shape.
from Graphics import * win = Window("Shapes", 200, 200) shape = Pie((100, 100), 90, 45, 360 - 45) shape.fill = Color("lightblue") shape.draw(win)
Draw an arc.
- Arc((x, y), radius, startDegree, stopDegree) - zero to right
- Arc(Point(x, y), radius, startDegree, stopDegree) - zero to right
from Graphics import * win = Window("Shapes", 200, 200) shape = Arc((100, 100), 90, 360 - 45, 360 + 45) shape.border = 3 shape.draw(win)
Sprites are graphical objects composed of costumes. Costumes are composed of 1 or more frames. If a costume has more than one frame, then it can be animated.
- Sprite() - create an empty sprite that you can add costumes
- Sprite(name) - create a sprite from the known name
- Sprite((x, y), name) - create a sprite at (x, y) from the known name.
There are a number of built-in sprites that you can create by using their name:
Each costume is composed of one or more "frames".
from Graphics import * win = Window("Shapes", 200, 200) sprite = Sprite((100, 100), "bear") sprite.draw(win)
Sprites have the following properties:
- name - the name of the sprite (eg, "bear")
- costume - name of current costume (eg, "left")
- shape - the current shape being drawn
- frame - number of current frame of animation (defaults to 0)
- costumes - dictionary of all costumes associated to the list of frames
The primary function of a sprite is that it can easily change costumes using these functions:
- addCostume(name, shape)
If a costume has multiple frames, then you can:
- animate(iterations, delay)
- animate(seconds, delay)
- animateTo((x, y), seconds)
- animateTo((x, y), seconds, delay)
You can also manually move between frames of an animation:
Additional frame methods:
Calico Graphics also has a set of GUI widgets. These have additional uses for providing control.
Draw an button, and alternatively connect a function to it. New in Calico 1.0.4; updated in 1.1.0
- Button((x, y), text)
- Button(Point(x, y), text)
You connect a function to it like so:
from Graphics import * win = Window() button = Button(Point(150, 150), "Press me!") button.draw(win) def printit(o, e): print(e) button.connect("click", printit)
See more about Gtk.Button here: http://docs.go-mono.com/?link=T%3aGtk.Button%2f*
Horizontal slider. New in Calico Graphics 1.1.0.
- HSlider((x,y), width)
- HSlider(Point(x,y), width)
from Graphics import * from Myro import getFilenames files = getFilenames("../images/brain/*.jpg") pics =  for file in files: pics.append(makePicture(file)) window = Window("Laura's Brain", pics.width, pics.height + 50) last = 0 pics[last].draw(window) pics[last].moveTo(pics.width/2, pics.height/2) slider = HSlider((0,pics.height), pics.width) slider.draw(window) def showimage(obj, event): global last v = event.value pos = int(v/101 * len(pics) ) if pos != last: window.undraw(pics[last]) pics[pos].draw(window) pics[pos].moveTo(pics.width/2, pics.height/2) last = pos slider.connect("change-value", showimage)
You can really do anything you want at this level. Calico is written using the Gtk, Graphics Tool Kit.
One complication with using the Gtk interactively is that there are some functions that can only be called in the Graphics Thread. To do that, you use the Gtk.Application.Invoke function. Here is a helper alias:
invoke = Gtk.Application.Invoke
import Gtk window = Gtk.Window("Title") vbox = Gtk.VBox() entry = Gtk.Entry() vbox.PackStart(entry) window.Add(vbox) invoke(lambda obj, event: window.ShowAll())
If you tried to run window.ShowAll() without invoking it in the Graphics Thread (by using the invoke function) then your application will eventually crash, and perhaps bring down all of Calico with it.
It isn't always obvious what needs to be invoked in the Graphics Thread.
You can build an entire GUI with the Gtk, including menus, buttons, entry fields... the whole of Calico even. To find out more about the low-level graphics Gtk API, see:
Groups of Shapes
There are two ways to group shapes: the Group and the Frame.
The Group is used for making a group out of existing shapes, so that you can easily perform a rotation to all of them.
The Frame is useful for creating a set of shapes all in the same frame of reference. Actually, a Frame is just an invisible Shape, and behaves like all shapes. You create a Frame, and then draw objects onto it.
- Frame(x, y)
- Frame((x, y))
You create a frame, and then draw onto it:
- frame = frame(150, 150)
from Graphics import * win = Window() car = Frame(150, 150) wheel1 = Rectangle((-10, -10), (10, -5)) wheel2 = Rectangle((-10, 10), (10, 5)) wheel1.draw(car) wheel2.draw(car) car.draw(win) car.rotate(45) car.forward(10)
from Graphics import * import time win = Window("Clock") win.mode = "manual" face = Circle((150, 150), 125) face.fill = None face.draw(win) s = Frame(150, 150) line = Line((0, 0), (100, 0)) line.color = Color("red") line.draw(s) m = Frame(150, 150) line = Line((0, 0), (75, 0)) line.color = Color("blue") line.border = 2 line.draw(m) h = Frame(150, 150) line = Line((0, 0), (50, 0)) line.color = Color("black") line.border = 3 line.draw(h) s.draw(win) m.draw(win) h.draw(win) def main(): while True: t = time.localtime() s.rotateTo(t/60 * 360 - 90) m.rotateTo(t/60 * 360 - 90) h.rotateTo(t/12 * 360 - 90) win.step(1) win.run(main)
from Graphics import * g = Graph() g.addEdge("Hello", "World") g.layout() g.draw()
- addEdge(from, to)
- draw(window | None)
- edges - the edges
- graph - the graph
- graph_count - count
- graphEdges - the edges
- graphNodes - the nodes
- layout() - call GraphViz to get layout
- layout(list) - call GraphViz to get Binary Tree layout of a list, where list is [left-tree, node, right-tree]
- layout(list, a, b, c) - call GraphViz to get Binary Tree layout of a list, where a is left-position, b is node, and c is right-position
- options - the options
- parser - the parser
- post_text - the text after GraphViz processing
- pre_text - the text before GraphViz processing
- processDot() - call the dot external
- recurseEdges() - internal call to process edges
- recurseNodes() - internal call to process nodes
- vertices - internal vertices
- window - the Graphics window, if one
(define show-tree (lambda (tree) (using "Graphics") (define! g (Graphics.Graph)) (g.layout tree 1 0 2) ;; left-index root-index right-index (g.draw))) (show-tree '(42 () ()))
Create a group of shapes.
- Group(shape1, shape2, ...)
By creating a group, you can move/change all of the shapes at once.
from Graphics import * win = Window("USFlag", 700, 400) def make_star(x, y, segment): arrow = Arrow(Point(x, y), 1) arrow.draw(win) arrow.penDown() for i in range(5): arrow.forward(segment) arrow.rotate(72) arrow.forward(segment) arrow.rotate(-72) arrow.rotate(-72) polygon = Polygon(*arrow.penUp()) polygon.draw(win) polygon.color = makeColor("white") arrow.undraw() return polygon for row in range(13): band = Rectangle(Point(0,row * 400/13), Point(700, row * 400/13 + 400/13)) band.draw(win) if row % 2 == 1: # odd, white band.color = makeColor("white") else: band.color = makeColor("red") blue = Rectangle(Point(0,0), Point(300, 214)) blue.color = makeColor("blue") blue.draw(win) stars =  for col in range(6): for row in range(9): if row % 2 == 1: # odd row if col == 5: continue x = col * 50 + 25 else: x = col * 50 y = row * 22 star = make_star(x + 10, y + 13, 5) stars.append(star)
And now to demonstrate the Group() constructor:
def animate(): g = Group(*stars) win.mode = "manual" for i in range(20): g.rotate(10) win.step(.5)
age = AnimatedGifEncoder()
Sets the delay time between each frame, or changes it for subsequent frames (applies to last frame added).
Sets the GIF frame disposal code for the last added frame and any subsequent frames. Default is 0 if no transparent color has been set, otherwise 2.
Sets the number of times the set of GIF frames should be played. Default is 1; 0 means play indefinitely. Must be invoked before the first image is added.
Sets the transparent color for the last added frame and any subsequent frames. Since all colors are subject to modification in the quantization process, the color in the final palette for each frame closest to the given color becomes the transparent color for that frame. May be set to null to indicate no transparent color.
Adds next GIF frame. The frame is not written immediately, but is actually deferred until the next frame is received so that timing data can be inserted. Invoking finish() flushes all frames. If setSize was not invoked, the size of the first image is used for all subsequent frames.
Flushes any pending data and closes output file. If writing to an OutputStream, the stream is not closed.
Sets frame rate in frames per second. Equivalent to setDelay(1000/fps).
Sets quality of color quantization (conversion of images to the maximum 256 colors allowed by the GIF specification). Lower values (minimum = 1) produce better colors, but slow processing significantly. 10 is the default, and produces good color mapping at reasonable speeds. Values greater than 20 do not yield significant improvements in speed.
Sets the GIF frame size. The default size is the size of the first frame added if this method is not invoked.
Initiates GIF file creation on the given stream. The stream is not closed automatically.
Initiates writing of a GIF file with the specified name.
Windows can operate in a number of modes.
You can also "run" the window, which automatically calls the window.step() function, or you can call your own function.
Shapes will be stacked in the order that they were drawn on the window (e.g., first drawn are on the bottom). However, one can change the order with the following methods:
- window.clear() - clears all shapes drawn on window
- window.stackOnTop(shape) - moves shape to top
- window.stackOnBottom(shape) - moves shape to bottom
from Graphics import * win = Window() win.setBackground(Color("black")) sun = Circle((150, 150), 50) sun.fill = Color("yellow") sun.draw(win) earth = Circle((70, 70), 20) earth.fill = Color("green") earth.draw(sun) moon = Circle((20, 20), 5) moon.fill = Color("grey") moon.draw(earth) pen = Pen(Color("white"), True) # True means that pen is down pen.draw(win) pen.stackOnBottom() def main(): win.mode = "manual" for s in range(360): sun.rotate(1) earth.rotate(5) win.step(.1) pen.appendPath(Point(moon.gx, moon.gy)) # gx,gy is global position in window, after render win.run(main)
The window can be in "physics" mode. The following window properties are then useful.
- win.gravity = Vector(x, y)
- win.time - total time of simulation so far
- win.simulationStepTime - time to advance simulation on each step (default is .01 seconds)
The following shape properties are then useful in "physics" mode.
- shape.body.ApplyForce(Vector(x, y))
- shape.wrap = boolean
- shape.bounce - 1.0 is 100% bounce; 0.0 is no bounce
- shape.friction - amount of friction
- http://www.youtube.com/watch?v=L3P5t8hAhe4 - Word Physics
- http://www.youtube.com/watch?v=Rl0tVqsf71g - Word Physics II
- http://www.youtube.com/watch?v=yLZJmyQEALA - Angry Blocks
- http://www.youtube.com/watch?v=XDHLbi1pl9U - In slow motion
Low Level Graphics
You can also create your own shapes in Calico Graphics. One easy way is to subclass the Shape class and override the render method, like so:
class MyShape(Shape): def render(self, cr): cr.LineTo(0, 0) cr.LineTo(100, 100) cr.Stroke()
Render takes a Cairo Graphics context (cr). You can read more about what you can do with Cairo Graphics. Those examples are in C#.
To convert to Calico Python:
- leave out the word "new"
- Color is the Calico Graphics Color so use Color(...).getCairo()
- semi-colons are optional
- don't list the type of variables
For example, this C# code:
cr.LineWidth = 0.1; cr.Color = new Color(0, 0, 0); cr.Rectangle(0.25, 0.25, 0.5, 0.5); cr.Stroke();
would become this Calico Python code:
cr.LineWidth = 0.1 cr.Color = Color(0, 0, 0).getCairo() cr.Rectangle(0.25, 0.25, 0.5, 0.5) cr.Stroke()
from Graphics import * win = Window() class MyShape(Shape): def render(self, cr): cr.LineTo(0, 0) cr.LineTo(100, 100) cr.Stroke() s = MyShape() s.draw(win)
The Plot object allows you to create a graph of data.
- Plot("Title", width, height)
from Graphics import Plot plot = Plot("Sample Plot", 600, 300) plot.xLabel.text = "time" plot.yLabel.text = "balls in the air" for data in [10, 30, 40, 50, 0, 100, 110, 40, 50]: plot.append(data)