""" rosegraphics.py - a simple Graphics library for Python. Its key feature is: -- USING this library provides a simple introduction to USING objects. Other key features include: -- It has a rich set of classes, methods and instance variables. In addition to classes like Circles that are natural for students, it has other kinds of classes like RoseWindow and SimpleTurtle to provide a richer set of examples than "just" a graphics library. -- It allows one to do a reasonable set of graphics operations with reasonable efficiency. The API mimics Java's Shape API for the most part. -- It is built on top of tkinter and its extension ttk (the standard graphics libraries that come with Python). -- Unlike tkinter, it is NOT event-driven and hence can be used before students see that paradigm. (There is a behind-the-scenes facility for listening for and responding to events, for those who want to do so.) -- It attempts to be as bullet-proof as possible, to make it easy for beginners to use it. In particular, it attempts to provide reasonable error messages when a student misuses the API. -- It was inspired by zellegraphics but is a complete re-implementation that attempts to: -- Be more bullet-proof. -- Provide a richer set of examples for using objects. -- Have an API that is more like Java's Shape API than tkinter's (older) API. -- While it can serve as an example for defining classes, it is NOT intended to do so for beginners. It is excellent for helping students learn to USE objects; it is NOT perfect for helping students learn to WRITE CLASSES. See the MAIN function below for typical examples of its use. Authors: David Mutchler, Mark Hays, Michael Wollowswki, Matt Boutell, Chandan Rupakheti, Claude Anderson and their colleagues, with thanks to John Zelle for inspiration and hints. First completed version: September 2014. """ import tkinter from tkinter import font as tkinter_font import time import turtle # ---------------------------------------------------------------------- # All the windows that are constructed during a run share the single # _master_Tk (a tkinter.Tk object) # as their common root. The first construction of a RoseWindow # sets this _master_Tk to a Tkinter.Tk object. # ---------------------------------------------------------------------- _master_Tk = None # ---------------------------------------------------------------------- # RoseWindow is the top-level object. It starts with a single RoseCanvas. # ---------------------------------------------------------------------- class RoseWindow(object): """ A RoseWindow is a window that pops up when constructed. It can have RoseWidgets on it and starts by default with a single RoseCanvas upon which one can draw shapes. To construct a RoseWindow, use: - rg.RoseWindow() or use any of its optional arguments, as in these examples: window = rg.RoseWindow(400, 300) # 400 wide by 300 tall window = rg.RoseWindow(400, 300, "Funny window") # with a title Instance variables include: width: width of this window (in pixels) height: width of this window (in pixels) title: displayed on the window's bar widgets: the things attached to this window """ def __init__(self, width=400, height=300, title="Rose Graphics", color="black", canvas_color="white", make_initial_canvas=True): """ Pops up a tkinter.Toplevel window with (by default) a RoseCanvas (and associated tkinter.Canvas) on it. Arguments are: -- width, height: dimensions of the window (in pixels). -- title: title displayed on the windoww. -- color: background color of the window -- canvas_color: background color of the canvas displayed on the window by default -- make_initial_canvas: -- If True, a default canvas is placed on the window. -- Otherwise, no default canvas is placed on the window. If this is the first RoseWindow constructed, then a hidden Tk object is constructed to control the event loop. Preconditions: :type width: int :type height: int :type title: str :type color: Color :type canvas_color: Color :type make_initial_canvas: bool """ # check_types([(width, (int, float)), # (height, (int, float)), # (title, (Color, str) # -------------------------------------------------------------- # The _master_Tk controls the mainloop for ALL the RoseWindows. # If this is the first RoseWindow constructed in this run, # then construct the _master_Tk object. # -------------------------------------------------------------- global _master_Tk if not _master_Tk: _master_Tk = tkinter.Tk() _master_Tk.withdraw() else: time.sleep(0.1) # Helps the window appear on TOP of Eclipse # -------------------------------------------------------------- # Has a tkinter.Toplevel, and a tkinter.Canvas on the Toplevel. # -------------------------------------------------------------- self.toplevel = tkinter.Toplevel(_master_Tk, background=color, width=width, height=height) self.toplevel.title(title) self._is_closed = False self.toplevel.protocol("WM_DELETE_WINDOW", self.close) # FIXME: The next two need to be properties to have # setting happen correctly. Really belongs to RoseCanvas. # See comments elsewhere on this. self.width = width self.height = height if make_initial_canvas: self.initial_canvas = RoseCanvas(self, width, height, canvas_color) else: self.initial_canvas = None self.widgets = [self.initial_canvas] # FIXME: Do any other tailoring of the toplevel as desired, # e.g. borderwidth and style... # -------------------------------------------------------------- # Catch mouse clicks and key presses. # -------------------------------------------------------------- self.mouse = Mouse() self.keyboard = Keyboard() self.toplevel.bind("