Module pacai.ui.gridworld.utils

Various graphics utilities for gridworld.

Expand source code
"""
Various graphics utilities for gridworld.
"""

import sys
import time
import tkinter

_root_window = None  # The root window for graphics output
_canvas = None  # The canvas which holds graphics
_canvas_xs = None  # Size of canvas object
_canvas_ys = None
_canvas_x = None  # Current position on canvas
_canvas_y = None
_canvas_col = None  # Current colour (set to black below)
_canvas_tsize = 12
_canvas_tserifs = 0

def formatColor(r, g, b):
    return '#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255))

def sleep(secs):
    global _root_window

    if (_root_window is None):
        time.sleep(secs)
    else:
        _root_window.update_idletasks()
        _root_window.after(int(1000 * secs), _root_window.quit)
        _root_window.mainloop()

def begin_graphics(width = 640, height = 480, color = formatColor(0, 0, 0), title = None):
    global _root_window, _canvas, _canvas_x, _canvas_y, _canvas_xs, _canvas_ys, _bg_color

    # Check for duplicate call
    if _root_window is not None:
        # Lose the window.
        _root_window.destroy()

    # Save the canvas size parameters
    _canvas_xs, _canvas_ys = width - 1, height - 1
    _canvas_x, _canvas_y = 0, _canvas_ys
    _bg_color = color

    # Create the root window
    _root_window = tkinter.Tk(baseName = 'pacman')
    _root_window.protocol('WM_DELETE_WINDOW', _destroy_window)
    _root_window.title(title or 'Graphics Window')
    _root_window.resizable(0, 0)

    # Create the canvas object
    try:
        _canvas = tkinter.Canvas(_root_window, width=width, height=height)
        _canvas.pack()
        draw_background()
        _canvas.update()
    except Exception as ex:
        _root_window = None
        raise RuntimeError("Unable to create tkinter canvas.") from ex

    # Bind to key-down and key-up events
    _root_window.bind("<KeyPress>", _keypress)
    _root_window.bind("<KeyRelease>", _keyrelease)
    _root_window.bind("<FocusIn>", _clear_keys)
    _root_window.bind("<FocusOut>", _clear_keys)
    _clear_keys()

def draw_background():
    corners = [(0, 0), (0, _canvas_ys), (_canvas_xs, _canvas_ys), (_canvas_xs, 0)]
    polygon(corners, _bg_color, fillColor=_bg_color, filled=True, smoothed=False)

def _destroy_window(event=None):
    sys.exit(0)

def clear_screen(background=None):
    global _canvas_x, _canvas_y
    _canvas.delete('all')
    draw_background()
    _canvas_x, _canvas_y = 0, _canvas_ys

def polygon(coords, outlineColor, fillColor=None, filled=1, smoothed=1, behind=0, width=1):
    c = []
    for coord in coords:
        c.append(coord[0])
        c.append(coord[1])

    if (fillColor is None):
        fillColor = outlineColor

    if filled == 0:
        fillColor = ""

    poly = _canvas.create_polygon(c, outline=outlineColor, fill=fillColor,
            smooth=smoothed, width=width)
    if behind > 0:
        _canvas.tag_lower(poly, behind)  # Higher should be more visible
    return poly

def circle(pos, r, outlineColor, fillColor, endpoints=None, style='pieslice', width=2):
    x, y = pos
    x0, x1 = x - r - 1, x + r
    y0, y1 = y - r - 1, y + r
    if (endpoints is None):
        e = [0, 359]
    else:
        e = list(endpoints)

    while e[0] > e[1]:
        e[1] = e[1] + 360

    return _canvas.create_arc(x0, y0, x1, y1, outline=outlineColor, fill=fillColor,
                              extent=e[1] - e[0], start=e[0], style=style, width=width)

def text(pos, color, contents, font='Helvetica', size=12, style='normal', anchor="nw"):
    global _canvas_x, _canvas_y

    x, y = pos
    font = (font, str(size), style)
    return _canvas.create_text(x, y, fill=color, text=contents, font=font, anchor=anchor)

def line(here, there, color=formatColor(0, 0, 0), width=2):
    x0, y0 = here[0], here[1]
    x1, y1 = there[0], there[1]
    return _canvas.create_line(x0, y0, x1, y1, fill=color, width=width)

###########################
#   Keypress handling     #
###########################

# We bind to key-down and key-up events.

_keysdown = {}
# This holds an unprocessed key release.  We delay key releases by up to
# one call to keys_pressed() to get round a problem with auto repeat.
_got_release = None

def _keypress(event):
    global _got_release
    _keysdown[event.keysym] = 1
    # print(event.char, event.keycode)
    _got_release = None

def _keyrelease(event):
    global _got_release

    try:
        del _keysdown[event.keysym]
    except Exception:
        pass

    _got_release = 1

def _clear_keys(event=None):
    global _keysdown, _got_release

    _keysdown = {}
    _got_release = None

def keys_pressed(d_o_e=None, d_w=tkinter._tkinter.DONT_WAIT):
    global _root_window

    if (d_o_e is None):
        d_o_e = _root_window.tk.dooneevent

    d_o_e(d_w)
    if _got_release:
        d_o_e(d_w)

    return list(_keysdown.keys())

# Block for a list of keys...

def wait_for_keys():
    keys = []
    while keys == []:
        keys = keys_pressed()
        sleep(0.05)
    return keys

Functions

def begin_graphics(width=640, height=480, color='#000000', title=None)
Expand source code
def begin_graphics(width = 640, height = 480, color = formatColor(0, 0, 0), title = None):
    global _root_window, _canvas, _canvas_x, _canvas_y, _canvas_xs, _canvas_ys, _bg_color

    # Check for duplicate call
    if _root_window is not None:
        # Lose the window.
        _root_window.destroy()

    # Save the canvas size parameters
    _canvas_xs, _canvas_ys = width - 1, height - 1
    _canvas_x, _canvas_y = 0, _canvas_ys
    _bg_color = color

    # Create the root window
    _root_window = tkinter.Tk(baseName = 'pacman')
    _root_window.protocol('WM_DELETE_WINDOW', _destroy_window)
    _root_window.title(title or 'Graphics Window')
    _root_window.resizable(0, 0)

    # Create the canvas object
    try:
        _canvas = tkinter.Canvas(_root_window, width=width, height=height)
        _canvas.pack()
        draw_background()
        _canvas.update()
    except Exception as ex:
        _root_window = None
        raise RuntimeError("Unable to create tkinter canvas.") from ex

    # Bind to key-down and key-up events
    _root_window.bind("<KeyPress>", _keypress)
    _root_window.bind("<KeyRelease>", _keyrelease)
    _root_window.bind("<FocusIn>", _clear_keys)
    _root_window.bind("<FocusOut>", _clear_keys)
    _clear_keys()
def circle(pos, r, outlineColor, fillColor, endpoints=None, style='pieslice', width=2)
Expand source code
def circle(pos, r, outlineColor, fillColor, endpoints=None, style='pieslice', width=2):
    x, y = pos
    x0, x1 = x - r - 1, x + r
    y0, y1 = y - r - 1, y + r
    if (endpoints is None):
        e = [0, 359]
    else:
        e = list(endpoints)

    while e[0] > e[1]:
        e[1] = e[1] + 360

    return _canvas.create_arc(x0, y0, x1, y1, outline=outlineColor, fill=fillColor,
                              extent=e[1] - e[0], start=e[0], style=style, width=width)
def clear_screen(background=None)
Expand source code
def clear_screen(background=None):
    global _canvas_x, _canvas_y
    _canvas.delete('all')
    draw_background()
    _canvas_x, _canvas_y = 0, _canvas_ys
def draw_background()
Expand source code
def draw_background():
    corners = [(0, 0), (0, _canvas_ys), (_canvas_xs, _canvas_ys), (_canvas_xs, 0)]
    polygon(corners, _bg_color, fillColor=_bg_color, filled=True, smoothed=False)
def formatColor(r, g, b)
Expand source code
def formatColor(r, g, b):
    return '#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255))
def keys_pressed(d_o_e=None, d_w=2)
Expand source code
def keys_pressed(d_o_e=None, d_w=tkinter._tkinter.DONT_WAIT):
    global _root_window

    if (d_o_e is None):
        d_o_e = _root_window.tk.dooneevent

    d_o_e(d_w)
    if _got_release:
        d_o_e(d_w)

    return list(_keysdown.keys())
def line(here, there, color='#000000', width=2)
Expand source code
def line(here, there, color=formatColor(0, 0, 0), width=2):
    x0, y0 = here[0], here[1]
    x1, y1 = there[0], there[1]
    return _canvas.create_line(x0, y0, x1, y1, fill=color, width=width)
def polygon(coords, outlineColor, fillColor=None, filled=1, smoothed=1, behind=0, width=1)
Expand source code
def polygon(coords, outlineColor, fillColor=None, filled=1, smoothed=1, behind=0, width=1):
    c = []
    for coord in coords:
        c.append(coord[0])
        c.append(coord[1])

    if (fillColor is None):
        fillColor = outlineColor

    if filled == 0:
        fillColor = ""

    poly = _canvas.create_polygon(c, outline=outlineColor, fill=fillColor,
            smooth=smoothed, width=width)
    if behind > 0:
        _canvas.tag_lower(poly, behind)  # Higher should be more visible
    return poly
def sleep(secs)
Expand source code
def sleep(secs):
    global _root_window

    if (_root_window is None):
        time.sleep(secs)
    else:
        _root_window.update_idletasks()
        _root_window.after(int(1000 * secs), _root_window.quit)
        _root_window.mainloop()
def text(pos, color, contents, font='Helvetica', size=12, style='normal', anchor='nw')
Expand source code
def text(pos, color, contents, font='Helvetica', size=12, style='normal', anchor="nw"):
    global _canvas_x, _canvas_y

    x, y = pos
    font = (font, str(size), style)
    return _canvas.create_text(x, y, fill=color, text=contents, font=font, anchor=anchor)
def wait_for_keys()
Expand source code
def wait_for_keys():
    keys = []
    while keys == []:
        keys = keys_pressed()
        sleep(0.05)
    return keys