Esempio n. 1
0
class GraphicsWindow(ActionListener, KeyListener, MouseInputListener):
    """
    Creates a :py:class:`~jygsaw.graphicswindow.GraphicsWindow` with a
    :py:class:`~jygsaw.graphicswindow.Canvas` object that can be drawn on.
    Takes a title, window width, and window height.
    An optional background color can be specified.
    """
    colors = [BLACK, BLUE, CYAN, DARK_GRAY, GRAY, GREEN,
              LIGHT_GRAY, MAGENTA, ORANGE, PINK, RED, WHITE, YELLOW]

    def __init__(self, title, w, h, bg_color=WHITE):
        assert w > 0, "GraphicsWindow width must be greater than zero"
        assert h > 0, "GraphicsWindow height must be greater than zero"

        self.objs = []  # List of GraphicsObjects
        self.bg_color = bg_color

        self.frame = JFrame(
            title, defaultCloseOperation=JFrame.EXIT_ON_CLOSE,
            size=(w, h))

        self.frame.setLocationRelativeTo(None)
        self.frame.contentPane = Canvas(self, self.objs, self.bg_color)
        self.frame.contentPane.setPreferredSize(Dimension(w, h))

        self.frame.addMouseListener(self)
        self.frame.addMouseMotionListener(self)
        self.frame.addKeyListener(self)

        # MouseEvent attributes
        self.mouse_x = 0
        self.mouse_y = 0
        self.mouse_buttons = Set()

        # Mouse callbacks
        self.on_mouse_clicked = None
        self.on_mouse_dragged = None
        self.on_mouse_moved = None
        self.on_mouse_pressed = None
        self.on_mouse_released = None
        self.on_mouse_exited = None
        self.on_mouse_entered = None

        # Key callbacks
        self.on_key_pressed = None
        self.on_key_released = None
        self.on_key_typed = None

        # Key values
        self.last_key_char = None
        self.last_key_code = None

        self.chars_pressed = Set()
        self.codes_pressed = Set()

        # Event queue
        self.event_queue = Queue()
        self.main_running = False

        # not needed, user_draw is called directly from on_draw
        self.on_draw = None

    def set_visible(self, visible):
        """Sets the window to visible."""
        assert isinstance(visible, bool), "Variable is not a boolean."
        self.frame.pack()
        self.frame.visible = visible

    def draw(self, *params):
        """
        Takes any number of :py:class:`~jygsaw.graphicsobject.GraphicsObject`
        or :py:class:`~jygsaw.shape.Shape` objects, or :py:class:`~jygsaw.group.Group`,
        and draws them on the Canvas. If a shape is drawn without specifying a color
        the default color is used. The default stroke option
        (:py:class:`True` or :py:class:`False`) and *stroke_color* is saved in each object.
        """
        for arg in params:
            if isinstance(arg, GraphicsObject) or isinstance(arg, Shape):
                if arg.color is None:
                    arg.color = self.frame.contentPane.default_color
                arg.stroke_color = self.frame.contentPane.stroke_color
                arg.stroke = self.frame.contentPane.stroke
                arg.filled = self.frame.contentPane.filled
                arg.stroke_width = self.frame.contentPane.stroke_width
                if isinstance(arg, Text):
                    arg.font = self.frame.contentPane.font
                    arg.size = self.frame.contentPane.text_size
                self.objs.append(arg)
            elif isinstance(arg, Group):
                for obj in arg.group:
                    if obj.color is None:
                        obj.color = self.frame.contentPane.default_color
                    obj.stroke_color = self.frame.contentPane.stroke_color
                    obj.stroke = self.frame.contentPane.stroke
                    obj.filled = self.frame.contentPane.filled
                    arg.stroke_width = self.frame.contentPane.stroke_width
                    if isinstance(arg, Text):
                        arg.font = self.frame.contentPane.font
                        arg.size = self.frame.contentPane.text_size
                    self.objs.append(obj)
            else:
                raise Exception("Passed in something that's not a Group or GraphicsObject.")

    def set_default_color(self, c):
        """Sets the default color of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(c, Color), "The object passed is not a Color object."
        self.frame.contentPane.default_color = c

    def set_stroke_color(self, c):
        """Sets the stroke color in the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(c, Color), "The object passed is not a Color object."
        self.frame.contentPane.stroke_color = c

    def set_stroke(self, b):
        """Turns stroke on or off in the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(b, bool), "Variable given is not a boolean."
        self.frame.contentPane.stroke = b

    def set_stroke_width(self, w):
        """Sets the width of the stroke in the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(w, int), "Variable given is not an integer."
        self.frame.contentPane.stroke_width = w

    def set_filled(self, f):
        """Turns fill on or off in the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(f, bool), "Variable given is not a boolean."
        self.frame.contentPane.filled = f

    def set_bg_color(self, c):
        """Sets the background color of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(c, Color), "The object passed is not a Color object."
        self.frame.contentPane.background_color = c
        self.background = c

    def set_font(self, f):
        """Sets the font of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        self.frame.contentPane.font = f

    def set_text_size(self, s):
        """Sets the text size of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert s >= 0 and isinstance(
            s, int), "Font size must be greater than or equal to 0"
        self.frame.contentPane.text_size = s

    def get_bg_color(self):
        """Returns the background color of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        return self.background

    def redraw(self, delay=0.0):
        """
        Redraws the :py:class:`~jygsaw.graphicswindow.Canvas`.
        Only returns when done. An optional float can also be used to sleep after redrawing.
        """
        # Use non-blocking redraw because there is no one-to-one relation
        # between calling cavas.repaint() and execution of paintComponent()
        #
        if SwingUtilities.isEventDispatchThread():
            self.frame.contentPane.repaint()
        else:
            self.frame.contentPane.blocking_redraw()
        sleep(delay)

    def clear(self):
        """
        Clears the screen so that only the background is visible.
        Also deletes all :py:class:`~jygsaw.graphicsobject.GraphicsObject` and :py:class:`~jygsaw.shape.Shape` and objects.
        """
        self.objs = []
        self.frame.contentPane.objs = self.objs

    def mouseEntered(self, e):
        """
        Runs when the mouse enters the window.
        Sets the mouse coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouse_entered` function, if any.
        """
        pos = self.frame.contentPane.getMousePosition()
        if pos:
            self.mouse_x = pos.x
            self.mouse_y = pos.y
        if self.main_running:
            if self.on_mouse_entered:
                self.on_mouse_entered()
        else:
            self.event_queue.put(e)
        if debug:
            print (self.mouse_x, self.mouse_y)

    def mouseClicked(self, e):
        """
        Runs when the mouse is clicked.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouse_clicked` function, if any.
        """
        pos = self.frame.contentPane.getMousePosition()
        if pos:
            self.mouse_x = pos.x
            self.mouse_y = pos.y
        if self.main_running:
            if self.on_mouse_clicked:
                self.on_mouse_clicked()
        else:
            self.event_queue.put(e)

    def mouseExited(self, e):
        """
        Runs when the mouse exits the window.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouse_exited` function, if any.
        """
        pos = self.frame.contentPane.getMousePosition()
        if pos:
            self.mouse_x = pos.x
            self.mouse_y = pos.y
        if self.main_running:
            if self.on_mouse_exited:
                self.on_mouse_exited()
        else:
            self.event_queue.put(e)

    def mousePressed(self, e):
        """
        Runs when the mouse button is pressed.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouse_pressed` function, if any.
        """
        pos = self.frame.contentPane.getMousePosition()
        if pos:
            self.mouse_x = pos.x
            self.mouse_y = pos.y
        self.mouse_buttons.add(e.getButton())
        if self.main_running:
            if self.on_mouse_pressed:
                self.on_mouse_pressed()
        else:
            self.event_queue.put(e)

    def mouseReleased(self, e):
        """
        Runs when the mouse button is released.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouse_released` function, if any.
        """
        pos = self.frame.contentPane.getMousePosition()
        if pos:
            self.mouse_x = pos.x
            self.mouse_y = pos.y
        self.mouse_buttons.remove(e.getButton())
        if self.main_running:
            if self.on_mouse_released:
                self.on_mouse_released()
        else:
            self.event_queue.put(e)

    def mouseMoved(self, e):
        """
        Runs when the mouse is moved.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouse_moved` function, if any.
        """
        pos = self.frame.contentPane.getMousePosition()
        if pos:
            self.mouse_x = pos.x
            self.mouse_y = pos.y
        if self.main_running:
            if self.on_mouse_moved:
                self.on_mouse_moved()
        else:
            self.event_queue.put(e)
        if debug:
            print '(%d, %d)' % (self.mouse_x, self.mouse_y)

    def mouseDragged(self, e):
        """
        Runs when the mouse is dragged.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouseDragged` function, if any.
        """
        pos = self.frame.contentPane.getMousePosition()
        if pos:
            self.mouse_x = pos.x
            self.mouse_y = pos.y
        if self.main_running:
            if self.on_mouse_dragged:
                self.on_mouse_dragged()
        else:
            self.event_queue.put(e)

    def keyTyped(self, e):
        """
        Sets the last key character and code when a key is typed. Calls a
        user-provided :py:meth:`keyTyped` function, if any.
        """
        if debug:
            print e.getKeyCode()

        if self.main_running:
            if self.on_key_typed:
                self.on_key_typed()
        else:
            self.event_queue.put(e)

    def keyPressed(self, e):
        """
        Sets the last key character and code when a key is pressed. Calls a
        user-provided :py:meth:`key_pressed` function, if any.
        """
        self.last_key_code = e.getKeyCode()
        self.last_key_char = e.getKeyChar()
        self.chars_pressed.add(e.getKeyText(e.getKeyCode()).upper())
        self.codes_pressed.add(e.getKeyCode())

        if debug:
            print "Key pressed:"
            print e.getKeyText(e.getKeyCode())
            print e.getKeyCode()

        if self.main_running:
            if self.on_key_pressed:
                self.on_key_pressed()
        else:
            self.event_queue.put(e)

    def keyReleased(self, e):
        """
        Sets the last key character and code when a key is released. Calls a
        user-provided :py:meth:`key_released` function, if any.
        """
        self.chars_pressed.remove(e.getKeyText(e.getKeyCode()).upper())
        self.codes_pressed.remove(e.getKeyCode())

        if debug:
            print "Key released:"
            print e.getKeyText(e.getKeyCode())
            print e.getKeyCode()

        if self.main_running:
            if self.on_key_released:
                self.on_key_released()
        else:
            self.event_queue.put(e)

    def _is_key_pressed(self):
        return bool(self.chars_pressed)

    isKeyPressed = property(_is_key_pressed, ':py:class:`True` if any keys are pressed, else :py:class:`False`.')

    def _get_width(self):
        """Get the width of the canvas."""
        return self.frame.contentPane.width

    width = property(_get_width, 'Width of Canvas in pixels.')

    def _get_height(self):
        """Get the height of the canvas."""
        return self.frame.contentPane.height

    height = property(_get_height, 'Height of Canvas in pixels.')
Esempio n. 2
0
class GraphicsWindow(ActionListener, KeyListener, MouseInputListener):
    """
    Creates a :py:class:`~jygsaw.graphicswindow.GraphicsWindow` with a :py:class:`~jygsaw.graphicswindow.Canvas`
    object that can be drawn on. Takes a title, window width, and window height.
    An optional background color can be specified.
    """
    def __init__(self, title, w, h, backgroundColor=white):
        assert w > 0, "GraphicsWindow width must be greater than zero"
        assert h > 0, "GraphicsWindow height must be greater than zero"

        self._SHAPELIST_MAX_LENGTH = 200

        self.objs = []  # List of GraphicsObjects
        self.backgroundColor = backgroundColor

        self.frame = JFrame(
            title, defaultCloseOperation=JFrame.EXIT_ON_CLOSE,
            size=(w, h))

        self.frame.setLocationRelativeTo(None)
        self.frame.contentPane = Canvas(self, self.objs, self.backgroundColor)
        self.frame.contentPane.setPreferredSize(Dimension(w, h))

        self.frame.addMouseListener(self)
        self.frame.addMouseMotionListener(self)
        self.frame.addKeyListener(self)

        # MouseEvent attributes
        self.mouseEventType = 0
        self.mouseX = 0
        self.mouseY = 0

        # Mouse callbacks
        self.onMouseClicked = None
        self.onMouseDragged = None
        self.onMouseMoved = None
        self.onMousePressed = None
        self.onMouseReleased = None
        self.onMouseDragged = None
        self.onMouseExited = None
        self.onMouseEntered = None

        # KeyEvent booleans keyPressed, keyTyped
        self.keyEventType = 0
        self.keyP = False
        self.keyT = False

        # Key callbacks
        self.onKeyPressed = None
        self.onKeyReleased = None
        self.onKeyTyped = None

        # Key values
        self.lastKeyChar = None
        self.lastKeyCode = None

        self.charsPressed = Set()

        # Event queue
        self.eventQueue = Queue()

        self.mainRunning = False

        # not needed, user_draw is /called directly from onDraw
        self.onDraw = None

    def setVisible(self, isVisible):
        """Sets the window to visible."""
        assert isinstance(isVisible, bool), "Variable is not a boolean."
        self.frame.pack()
        self.frame.visible = isVisible

    def draw(self, *params):
        """
        Takes any number of :py:class:`~jygsaw.graphicsobject.GraphicsObject`
        or :py:class:`~jygsaw.shape.Shape` objects, or :py:class:`~jygsaw.group.Group`,
        and draws them on the Canvas. If a shape is drawn without specifying a color
        the default color is used. The default stroke option
        (:py:class:`True` or :py:class:`False`) and *strokeColor* is saved in each object.
        """
        for arg in params:
            if isinstance(arg, GraphicsObject) or isinstance(arg, Shape):
                if arg.color is None:
                    arg.color = self.frame.contentPane.defaultColor
                arg.strokeColor = self.frame.contentPane.strokeColor
                arg.stroke = self.frame.contentPane.stroke
                arg.filled = self.frame.contentPane.filled
                arg.strokeWidth = self.frame.contentPane.strokeWidth
                if isinstance(arg, Text):
                    arg.font = self.frame.contentPane.font
                    arg.size = self.frame.contentPane.textSize
                self.objs.append(arg)

                if len(self.objs) > self._SHAPELIST_MAX_LENGTH:
                    warn("You have more than " + str(self._SHAPELIST_MAX_LENGTH) +
                         " to be drawn. You may want to add a clearHalf() call to your" +
                         " code to avoid slowing your system.")

            elif isinstance(arg, Group):
                for obj in arg.group:
                    if obj.color is None:
                        obj.color = self.frame.contentPane.defaultColor
                    obj.strokeColor = self.frame.contentPane.strokeColor
                    obj.stroke = self.frame.contentPane.stroke
                    obj.filled = self.frame.contentPane.filled
                    arg.strokeWidth = self.frame.contentPane.strokeWidth
                    if isinstance(arg, Text):
                        arg.font = self.frame.contentPane.font
                        arg.size = self.frame.contentPane.textSize
                    self.objs.append(obj)

                    if len(self.objs) > self._SHAPELIST_MAX_LENGTH:
                        warn("You have more than " + str(self._SHAPELIST_MAX_LENGTH) +
                             " to be drawn. You may want to add a clearHalf() call to your" +
                             " code to avoid slowing your system.")
            else:
                raise Exception("Passed in something that's not a Group or GraphicsObject.")

    def setDefaultColor(self, c):
        """Sets the default color of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(c, Color), "The object passed is not a Color object."
        self.frame.contentPane.defaultColor = c

    def setStrokeColor(self, c):
        """Sets the stroke color in the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(c, Color), "The object passed is not a Color object."
        self.frame.contentPane.strokeColor = c

    def setStroke(self, b):
        """Turns stroke on or off in the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(b, bool), "Variable given is not a boolean."
        self.frame.contentPane.stroke = b

    def setStrokeWidth(self, w):
        """Sets the width of the stroke in the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(w, int), "Variable given is not an integer."
        self.frame.contentPane.strokeWidth = w

    def setFilled(self, f):
        """Turns fill on or off in the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(f, bool), "Variable given is not a boolean."
        self.frame.contentPane.filled = f

    def setBackgroundColor(self, c):
        """Sets the background color of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert isinstance(c, Color), "The object passed is not a Color object."
        self.frame.contentPane.backgroundColor = c
        self.background = c

    def setFont(self, f):
        """Sets the font of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        self.frame.contentPane.font = f

    def setTextSize(self, s):
        """Sets the text size of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        assert s >= 0 and isinstance(
            s, int), "Font size must be greater than or equal to 0"
        self.frame.contentPane.textSize = s

    def getBackgroundColor(self):
        """Returns the background color of the :py:class:`~jygsaw.graphicswindow.Canvas`."""
        return self.background

    def redraw(self, delay=0.0):
        """
        Redraws the :py:class:`~jygsaw.graphicswindow.Canvas`.
        Only returns when done. An optional float can also be used to sleep after redrawing.
        """
        # Use non-blocking redraw because there is no one-to-one relation
        # between calling cavas.repaint() and execution of paintComponent()
        #
        if SwingUtilities.isEventDispatchThread():
            self.frame.contentPane.repaint()
        else:
            self.frame.contentPane.blocking_redraw()
        sleep(delay)

    def clear(self):
        """
        Clears the screen so that only the background is visible.
        Also deletes all :py:class:`~jygsaw.graphicsobject.GraphicsObject` and :py:class:`~jygsaw.shape.Shape` and objects.
        """
        self.objs = []
        self.frame.contentPane.objs = self.objs

    def clearHalfIfFull(self):
        """Clears the window of half of its shapes if the shape list has more shapes
        than the window's limit.
        """

        if len(self.objs) >= self._SHAPELIST_MAX_LENGTH:
            self.objs = self.objs[len(self.objs)/2:]
            self.frame.contentPane.objs = self.objs

    def mouseEntered(self, e):
        """
        Runs when the mouse enters the window.
        Sets the mouse coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouseEntered` function, if any.
        """
        self.mouseEventType = e.getID()
        self.mouseX = e.getX()
        self.mouseY = e.getY() - 25
        if self.mainRunning:
            if self.onMouseEntered:
                self.onMouseEntered()
        else:
            self.eventQueue.put(e)
        if debug:
            print (self.mouseX, self.mouseY)

    def mouseClicked(self, e):
        """
        Runs when the mouse is clicked.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouseClicked` function, if any.
        """
        self.mouseEventType = e.getID()
        self.mouseX = e.getX()
        self.mouseY = e.getY() - 25
        if self.mainRunning:
            if self.onMouseClicked:
                self.onMouseClicked()
        else:
            self.eventQueue.put(e)

    def mouseExited(self, e):
        """
        Runs when the mouse exits the window.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouseExited` function, if any.
        """
        self.mouseEventType = e.getID()
        self.mouseX = e.getX()
        self.mouseY = e.getY() - 25
        if self.mainRunning:
            if self.onMouseExited:
                self.onMouseExited()
        else:
            self.eventQueue.put(e)

    def mousePressed(self, e):
        """
        Runs when the mouse button is pressed.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mousePressed` function, if any.
        """
        self.mouseEventType = e.getID()
        self.mouseX = e.getX()
        self.mouseY = e.getY() - 25
        if self.mainRunning:
            if self.onMousePressed:
                self.onMousePressed()
        else:
            self.eventQueue.put(e)

    def mouseReleased(self, e):
        """
        Runs when the mouse button is released.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouseReleased` function, if any.
        """
        self.mouseEventType = e.getID()
        if self.mainRunning:
            if self.onMouseReleased:
                self.onMouseReleased()
        else:
            self.eventQueue.put(e)

    def mouseMoved(self, e):
        """
        Runs when the mouse is moved.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouseMoved` function, if any.
        """
        self.mouseEventType = e.getID()
        self.mouseX = e.getX()
        self.mouseY = e.getY() - 25
        if self.mainRunning:
            if self.onMouseMoved:
                self.onMouseMoved()
        else:
            self.eventQueue.put(e)
        if debug:
            print '(%d, %d)' % (self.mouseX, self.mouseY)

    def mouseDragged(self, e):
        """
        Runs when the mouse is dragged.
        Sets the mouse X and Y coordinates to the current mouse position.
        Calls a user-provided :py:meth:`mouseDragged` function, if any.
        """
        self.mouseEventType = e.getID()
        self.mouseX = e.getX()
        self.mouseY = e.getY() - 25
        if self.mainRunning:
            if self.onMouseDragged:
                self.onMouseDragged()
        else:
            self.eventQueue.put(e)

    def keyTyped(self, e):
        """
        Sets the last key character and code when a key is typed. Calls a
        user-provided :py:meth:`keyTyped` function, if any.
        """
        self.keyEventType = e.getID()

        if debug:
            print e.getKeyCode()

        if self.mainRunning:
            if self.onKeyTyped:
                self.onKeyTyped()
        else:
            self.eventQueue.put(e)

    def keyPressed(self, e):
        """
        Sets the last key character and code when a key is pressed. Calls a
        user-provided :py:meth:`keyPressed` function, if any.
        """
        self.keyEventType = e.getID()
        self.lastKeyCode = e.getKeyCode()
        self.lastKeyChar = e.getKeyChar()
        self.charsPressed.add(e.getKeyText(e.getKeyCode()).upper())

        if debug:
            print "Key pressed:"
            print e.getKeyText(e.getKeyCode())
            print e.getKeyCode()

        if self.mainRunning:
            if self.onKeyPressed:
                self.onKeyPressed()
        else:
            self.eventQueue.put(e)

    def keyReleased(self, e):
        """
        Sets the last key character and code when a key is released. Calls a
        user-provided :py:meth:`keyReleased` function, if any.
        """
        self.keyEventType = e.getID()
        self.charsPressed.remove(e.getKeyText(e.getKeyCode()).upper())

        if debug:
            print "Key released:"
            print e.getKeyText(e.getKeyCode())
            print e.getKeyCode()

        if self.mainRunning:
            if self.onKeyReleased:
                self.onKeyReleased()
        else:
            self.eventQueue.put(e)

    def _is_key_pressed(self):
        return bool(self.charsPressed)

    isKeyPressed = property(_is_key_pressed, ':py:class:`True` if any keys are pressed, else :py:class:`False`.')

    def _get_width(self):
        """Get the width of the canvas."""
        return self.frame.contentPane.width

    width = property(_get_width, 'Width of Canvas in pixels.')

    def _get_height(self):
        """Get the height of the canvas."""
        return self.frame.contentPane.height

    height = property(_get_height, 'Height of Canvas in pixels.')