Skip to content

Instantly share code, notes, and snippets.

@KevinTyrrell
Created April 27, 2017 12:30
Show Gist options
  • Save KevinTyrrell/68d3e5b55c1fd5fc367b079662f34fc2 to your computer and use it in GitHub Desktop.
Save KevinTyrrell/68d3e5b55c1fd5fc367b079662f34fc2 to your computer and use it in GitHub Desktop.
# Analysis: I had a lot of fun re-designing assignment 7 in this assignment. I created a Rectangle2D class and made it so that the back-end communicated with the front end. When the back-end was updated, so was the front-end. The only problem at that point was making sure I only used the back-end variables are didn't touch the front end.
# Design: Made a Rectangle2D class. Allowed the front end variables to be setup inside that class. Finally, added the ability for the text input fields to directly have access to the last clicked rectangle.
# Coding: Please indent and format your code properly
from tkinter import *
class Rectangle2D:
"""
Constructor function.
"""
def __init__(self, centerX, centerY, width, height):
self.__centerX = centerX
self.__centerY = centerY
self.__width = width
self.__height = height
# GUI variables.
self.__canvas = None
self.__rectptr = None
"""
Setter functions.
"""
def setCenter(self, x, y):
self.__centerX = x
self.__centerY = y
if (self.__synced()):
self.__update()
def setWidth(self, newVal):
self.__width = newVal
if (self.__synced()):
self.__update()
def setHeight(self, newVal):
self.__height = newVal
if (self.__synced()):
self.__update()
"""
Getter functions.
"""
def getCenterX(self):
return self.__centerX
def getCenterY(self):
return self.__centerY
def getWidth(self):
return self.__width
def getHeight(self):
return self.__height
"""
Translates the rectangle by dx, dy.
Updates the rectangle if synced.
Return - No value.
"""
def move(self, dx, dy):
self.__centerX += dx
self.__centerY += dy
if (self.__synced()):
self.__update()
"""
Returns true if two given rectangles are overlapping.
Two rectangles overlap if the assumption is false that one rectangle
is above, below, or on one of the sides of the other rectangle.
"""
def intersects(self, other_rect):
return (self.__centerY + self.__height / 2 > other_rect.__centerY - other_rect.__height / 2
and self.__centerY - self.__height / 2 < other_rect.__centerY + other_rect.__height / 2
and self.__centerX + self.__width / 2 > other_rect.__centerX - other_rect.__width / 2
and self.__centerX - self.__width / 2 < other_rect.__centerX + other_rect.__width / 2)
"""
Syncs this Rectangle2D with a tkinter rectangle.
The sync is only one way: Rectangle2D ---> tkinter Rectangle.
Changes to the tkinter rectangle will not update this Rectangle2D.
If this Rectangle2D changes, the tkinter Rectangle will be updated.
canvas - Canvas in which the `rect` exists on.
rect - Rectangle which this syncs to.
Return - No value.
"""
def sync(self, canvas, rect):
self.__canvas = canvas
self.__rectptr = rect
self.__update()
"""
Returns true if the Rectangle2D is synced with the tkinter rectangle.
"""
def __synced(self):
return self.__canvas != None and self.__rectptr != None
"""
Updates the front-end tkinter rectangle according to the Rectangle2D's properties.
Raises an exception if the Rectangle2D was never synced with tkinter's canvas.
Return - No value.
"""
def __update(self):
if (not self.__synced()):
raise Exception()
left_x = self.__centerX - self.__width / 2
left_y = self.__centerY - self.__height / 2
right_x = self.__centerX + self.__width / 2
right_y = self.__centerY + self.__height / 2
# Move the rectangle.
self.__canvas.coords(
self.__rectptr, (left_x, left_y, right_x, right_y))
class Application:
RECT_THICKNESS = 3
INTERSECT = "Rectangles are intersecting"
NOT_INTERSECT = "Rectangles are not intersecting"
def __init__(self, master, width, height):
# Frame that contains all other frames.
root = Frame(master)
input_frame = Frame(root)
# Properties
self.__label = Label(root, font = "Calibri 20 bold")
self.__canvas = Canvas(root)
self.__moving = None
self.__current_rect = None
self.__rectangles = {}
self.__inputs = []
# Window Specifications
master.resizable(False, False)
master.minsize(width, height)
master.maxsize(width, height)
# Packing.
root.pack()
self.__label.pack()
self.__canvas.pack(fill = BOTH, expand = YES)
input_frame.pack()
# Canvas settings
self.__canvas.config(height = height * 0.75, width = width * 0.75, highlightbackground = "RED")
# Function which initializes movement of rectangles via the mouse.
def callback_move_start(event):
"""
Determine the back-end rectangle that corresponds to the
front-end rectangle that lies underneath the mouse cursor.
"""
selected = self.__canvas.find_withtag(CURRENT)
# User did not select anything.
if (len(selected) == 0):
self.__current_rect = None
return
self.__current_rect = self.__rectangles[selected[0]]
self.__current_rect.setCenter(event.x, event.y)
# Function which moves rectangles via the mouse during drag events.
def callback_move_drag(event):
rect = self.__current_rect
if (rect == None):
return
rect.setCenter(event.x, event.y)
self.__check_collision()
self.__canvas.bind("<Button-1>", lambda event: callback_move_start(event))
self.__canvas.bind("<B1-Motion>", lambda event: callback_move_drag(event))
# Input fields.
LABELS_TEXT = ("Center x", "Center y", "Width", "Height")
for i in range(len(LABELS_TEXT)):
def callback(sv, field):
rect = self.__current_rect
# If no rectangle is clicked, disregard this.
if (rect == None):
return
# Make sure the input is valid.
newValue = None
try:
newValue = int(sv.get())
except ValueError:
return
if (field == 0):
rect.setCenter(newValue, rect.getCenterY())
elif (field == 1):
rect.setCenter(rect.getCenterX(), newValue)
elif (field == 2):
rect.setWidth(newValue)
else:
rect.setHeight(newValue)
# Variable which controls the text of the entry.
sv = StringVar()
# Whenever the text changes, notify the callback function.
sv.trace("w", lambda name, index, mode, sv = sv, field = i: callback(sv, field))
lbl = Label(input_frame, font = "Calibri 15", text = LABELS_TEXT[i])
input = Entry(input_frame, textvariable = sv)
lbl.grid(row = i)
input.grid(column = 1, row = i)
"""
Adds the provided rectangle to the graphical interface.
rect - Rectangle2D object.
color - Color of the rectangle.
Return - No value.
"""
def addRectangle(self, rect, color):
# Create an empty front-end rectangle.
tk_rect = self.__canvas.create_rectangle(0, 0, 0, 0,
width = self.RECT_THICKNESS, outline = "black",
fill = color, tags = ('rect'))
rect.sync(self.__canvas, tk_rect)
self.__rectangles[tk_rect] = rect
self.__check_collision()
"""
Changes the label of the GUI according to if rectangles
are currently intersecting or not. Checks each rectangle
with all other rectangles to determine collision.
Return - No value.
"""
def __check_collision(self):
for k1, v1 in self.__rectangles.items():
for k2, v2 in self.__rectangles.items():
if (v1 == v2):
continue
if (v1.intersects(v2)):
self.__label.config(text = self.INTERSECT)
return
self.__label.config(text = self.NOT_INTERSECT)
def main():
WIN_WIDTH = 600
WIN_HEIGHT = 700
root = Tk()
a = Application(root, WIN_WIDTH, WIN_HEIGHT)
a.addRectangle(Rectangle2D(50, 50, 30, 40), "green")
a.addRectangle(Rectangle2D(150, 150, 90, 90), "pink")
root.mainloop()
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment