Skip to content

Commit

Permalink
-whitespace, nicer syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
ES-Alexander committed Mar 31, 2020
1 parent 7614ed2 commit 4a68878
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 60 deletions.
20 changes: 10 additions & 10 deletions Lessons/L9/GUI_Example/Controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(self, master, graph_img=None):
automatic line detection, saving of state (to reopen where you left
off), or full automation with automatic axis detection and line
detection combined.
Constructor: Controller(tk.Tk, *str)
'''
Expand All @@ -36,15 +36,15 @@ def _add_bind_functions(self):
self._add_bind_functions() -> None
'''
self._view.add_binding_func('save_data', self._model.save_data)
self._view.add_binding_func('clear_data',self._model.clear_data)
self._view.add_binding_func('new_point', self._model.add_stored_point)
self._view.add_binding_func('del_point',
self._model.remove_stored_point)
self._view.add_binding_func('clear_stored_pts',
self._model.clear_stored_points)
self._view.add_binding_func('set_def_pts',
self._model.set_defining_points)
self._view.add_binding_funcs(
save_data = self._model.save_data,
clear_data = self._model.clear_data,
clear_stored_pts = self._model.clear_stored_points,
set_def_pts = self._model.set_defining_points,
new_point = self._model.add_stored_point,
del_point = self._model.remove_stored_point,
)


if __name__ == '__main__':
root = tk.Tk()
Expand Down
17 changes: 8 additions & 9 deletions Lessons/L9/GUI_Example/Model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ def __init__(self):
Users specify defining points mapping the pixel-plane to the graph-
plane. The Model stores the mapping, and converts additional pixel-
point values to graph-points.
Constructor: Model()
'''
# initialise memory and points
self.clear_data()
Expand Down Expand Up @@ -74,14 +74,14 @@ def remove_stored_point(self, pixel_point, max_dist=10):

# try to remove the exact point
point_removed = self._stored_points.pop(pixel_point, None)

if point_removed is None:
# pixel_point is not a key in stored points, find nearest
closest_point, dist = Model.get_closest(pixel_point,
list(self._stored_points.keys()))
if dist < max_dist:
point_removed = self._stored_points.pop(closest_point)

return point_removed

def _update_stored_points(self):
Expand Down Expand Up @@ -193,8 +193,7 @@ def _verify_points_map(points_map):
# check graph values are integers/floats
for graph_point in points_map.values():
Model._verify_point(graph_point)



@staticmethod
def _verify_point(point, value_type=None):
''' Checks if a point is valid.
Expand All @@ -221,7 +220,7 @@ def _verify_point(point, value_type=None):
"point values should be {}.".format(
value_type)))
return # no issues, continue where left off

raise Exception(("Invalid point length: {}".format(num_vals),
"- points should have 2 values"))

Expand Down Expand Up @@ -262,7 +261,7 @@ def get_closest(check_point, search_points):
cp = np.matrix(check_point)
sps = np.matrix(search_points)
diff = sps - cp

# get a list of the distance between check_point and each search point
dists = [np.linalg.norm(i) for i in diff]
# 'zip' joins N iterables for the first m elements, where m is the
Expand All @@ -272,5 +271,5 @@ def get_closest(check_point, search_points):
# the values of dists). Also available in 'max' function.
min_dist, closest_point = min(zip(dists,search_points),
key=lambda dist_sp: dist_sp[0])

return (closest_point, min_dist)
90 changes: 49 additions & 41 deletions Lessons/L9/GUI_Example/View.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ class View(object):
# the set of valid external bindings used in the class
external_bindings = ('save_data', 'clear_data', 'clear_stored_pts',
'set_def_pts', 'new_point', 'del_point')
INVALID_BINDING_MSG = "Invalid binding '{}'\nValid bindings are found " \
"using View.external_bindings."
# the set of valid internal bindings used in the class
_internal_bindings = ('define', 'add', 'delete')

def __init__(self, master, graph_img=None):
''' A class for managing the display of a graph-analysis GUI.
Expand All @@ -42,15 +44,15 @@ def _init_master(self, master):
'''
self._master = master # set the overarching master of the view
self._master.title("Data From Graph") # set a title for the view

# set the window to be 2/3 of the full size of the screen (int division)
sw = self._master.winfo_screenwidth()
sh = self._master.winfo_screenheight()
self._master.geometry("{}x{}".format(sw*2//3,sh*2//3))

# turn off resizing in the x and y directions
self._master.resizable(0, 0)

# make GUI as tall as window
self._master.rowconfigure(0, weight=1)

Expand Down Expand Up @@ -101,7 +103,7 @@ def _setup_display(self):
| | ------- |
| | errors |
+---------------+---------+
self._setup_display() -> None
'''
Expand Down Expand Up @@ -168,7 +170,7 @@ def _setup_def_pt_controls(self, master):
heading = tk.Label(master, text='Set Defining Points')
grid_frame = tk.Frame(master)

# pack the heading and frame into the control frame grid
# put the heading and frame into the control frame grid
heading.grid()
grid_frame.grid()

Expand Down Expand Up @@ -207,10 +209,10 @@ def _def_pt_submit(self, index):
self._errors.set('Please set a defining point location on the ' +
'graph before submitting')
return # invalid starting point - do not continue

# update controls
self._def_pt_controls[index].submit()

# check if all 3 points defined (ready to submit to external Model)
currently_defined = self._graph_canvas.find_withtag('define')
if len(currently_defined) == 3:
Expand Down Expand Up @@ -260,10 +262,10 @@ def _reset_def_points(self):
# reset controls
for point in range(3):
self._def_pt_controls[point].reset()

# set to defining first def pt
self._def_pt_select(0)

def _setup_control_buttons(self, master):
''' Initialises the stored-points control buttons.
Expand Down Expand Up @@ -296,7 +298,7 @@ def _setup_error_window(self, master):
self._errors.set('')
tk.Label(master, textvariable=self._errors, fg='red', justify=tk.LEFT,
wraplength=200).grid(pady=10)

def _setup_menubar(self):
''' Initialises the menubar with the desired menus.
Expand Down Expand Up @@ -328,7 +330,7 @@ def _setup_filemenu(self):
filemenu.add_separator() # add a separation line (denote sections)
filemenu.add_command(label="Save Data", command=self._save_data)
filemenu.add_command(label="Save Graph", command=self._save_graph)

# add to the menubar
self._menubar.add_cascade(label="File", menu=filemenu)

Expand Down Expand Up @@ -373,23 +375,23 @@ def _set_img(self, graph_img=None):
title = "Select graph image",
filetypes = (("JPEG image","*.jpg"), ("PNG image","*.png"),
("All Files","*.*")))

img = Image.open(graph_img) # extract image as PIL Image
# scale image to take up the full canvas (stretching is fine)
size = (self._graph_canvas.winfo_width(),
self._graph_canvas.winfo_height())
resized = img.resize(size, resample=Image.BILINEAR)
# convert to tkinter PhotoImage, store externally to make visible
self._img = PhotoImage(resized)

# add the image to the canvas (modify existing if possible)
canvas_image = self._graph_canvas.find_withtag('graph_image')
if canvas_image:
self._graph_canvas.itemconfig(canvas_image[0],image=self._img)
else:
self._graph_canvas.create_image(0, 0, image=self._img, anchor=NW,
tags='graph_image')

# new graph specified, so re-initialise data and view
self._clear_data() # clear all data
self._reset_def_points() # reset defining points controls
Expand All @@ -406,14 +408,14 @@ def _save_data(self, filename=None):
filename = tk.filedialog.asksaveasfilename(title = "Save Data As",
filetypes = (("Comma Separated Value","*.csv"),
("All Files","*.*")))

self._call_bind_func('save_data', filename)

def _clear_data(self):
''' Clears all data from the graph display and model.
self._clear_data() -> None
'''
self._graph_canvas.delete('point') # delete all view points
self._call_bind_func('clear_data') # clear external point data
Expand All @@ -439,7 +441,7 @@ def _clear_defining_points(self):
'''
# delete all points with tag 'define'
self._graph_canvas.delete('define')
self._graph_canvas.delete('define')

def _add_stored_point(self, pixel_point):
''' Add a new stored point to the external data and the graph.
Expand All @@ -451,7 +453,7 @@ def _add_stored_point(self, pixel_point):
'''
# check if point can be added (try adding to external data)
point_added = self._call_bind_func('new_point', pixel_point)

if point_added:
self._draw_point(pixel_point, {'outline':'','fill':'blue',
'tags':('stored','point')})
Expand All @@ -476,7 +478,7 @@ def _draw_point(self, pixel_point, properties):
'pixel_point' should be (x,y) coordinates of the point
'properties' should be a dictionary of desired properties of the point
self._draw_point(tuple(int,int), dict) ->
self._draw_point(tuple(int,int), dict) -> None
'''
ppx, ppy = pixel_point # extract coordinates
Expand All @@ -493,10 +495,10 @@ def _delete_point(self, pixel_point, max_dist=5):
if not self._graph_canvas.find_withtag('stored'):
# no stored points defined
return

# remove the point from the external data storage
point_removed = self._call_bind_func('del_point', pixel_point, max_dist)

if point_removed:
# if a point was removed, it must satisfy the max_dist condition,
# so remove the closest point (to pixel_point) from the view
Expand Down Expand Up @@ -542,7 +544,7 @@ def _graph_click(self, event):
if self._mode == 'add':
pixel_point = (event.x, event.y)
self._delete_point(pixel_point)

def _call_bind_func(self, binding, *args, **kwargs):
''' Call a binding function with the given arguments.
Expand All @@ -562,7 +564,7 @@ def _display_instructions(self):
''' Display usage instructions on startup.
self._display_instructions() -> None
'''
instructions = 'Choose 3 defining points which specify the plane ' +\
'your graph is on (click to select point location, then specify ' +\
Expand All @@ -571,22 +573,28 @@ def _display_instructions(self):
'pressing File/Save Data or File/Save Graph to save.'
self._errors.set(instructions)

def add_binding_func(self, binding, callback):
''' Adds the specified callback to the given binding, if valid.
def add_binding_funcs(self, **bindings_map):
''' Adds the bindings and callbacks in 'bindings_map'.
A valid binding must be in the tuple returned by View.external_bindings.
If any bindings are invalid (ie they are not found in
View.external_bindings), raises Exception.
Raises Exception on invalid binding.
self.add_binding_funcs(**binding=func) -> None
'''
# update but with validity check first in a dictionary comprehension
self._bindings.update({binding: callback if binding in self._bindings \
else self.invalid_binding(binding) \
for binding, callback in bindings_map.items()})

self.add_binding_func(str, func) -> None
@classmethod
def invalid_binding(cls, binding):
''' Raises Exception specifying that 'binding' is invalid.
cls.invalid_binding(str)
'''
if binding in View.external_bindings:
self._bindings[binding] = callback
else:
raise Exception('Invalid binding option {!r} -'.format(binding),
'Valid bindings are found using ',
'View.external_bindings')
raise Exception(cls.INVALID_BINDING_MSG.format(binding))

@staticmethod
def snapshot(widget, filename, scale=None):
Expand All @@ -605,7 +613,7 @@ def snapshot(widget, filename, scale=None):
scale = 2 # computer is a mac
else:
scale = 1 # assume everything is fine

# determine minimum bounds of widget
x_min = widget.winfo_rootx() * scale
y_min = widget.winfo_rooty() * scale
Expand All @@ -619,7 +627,7 @@ def snapshot(widget, filename, scale=None):
save_image.save(filename)
except Exception:
save_image.save(filename + '.png')


class DefPointControl(object):
''' A class for a defining point control in a View. '''
Expand All @@ -635,11 +643,11 @@ def __init__(self, master, r):
# initialise external functinos to do nothing until set externally
self._view_submit = lambda: None
self._view_select = lambda: None

# set up some variables to easily extract Entry values later
xr = tk.StringVar(); yr = tk.StringVar()
xr = tk.StringVar(); yr = tk.StringVar()
xr.set('x{}'.format(r)); yr.set('y{}'.format(r))

# create "Def Pt r (_,_)"
self._button = tk.Button(master, text='Def Pt {}'.format(r), width=9,
command=lambda: self._view_select(self._id))
Expand Down Expand Up @@ -702,7 +710,7 @@ def submit(self):
command=lambda: self._view_select(self._id))
for entry in self._entries:
entry.config(state='disabled')

def reset(self):
''' The internal reset function for this defining point.
Expand Down

0 comments on commit 4a68878

Please sign in to comment.