import tkinter as tk from tkinter import filedialog from tkinter import * import tkinter.ttk as ttk from tkinter.scrolledtext import ScrolledText import copy import cv2 from functools import partial import math import numpy as np import numpy from PIL import Image, ImageTk import string import tgc_definitions import tgc_tools import lidar_map_api import tgc_image_terrain from tgc_visualizer import drawCourseAsImage import OSMTGC TGC_GUI_VERSION = "0.3.2" image_width = 500 image_height = 500 # Make placeholder image data=numpy.array(numpy.random.random((image_width,image_height))*100,dtype=int) iim=Image.frombytes('L', (data.shape[1],data.shape[0]), data.astype('b').tostring()) root = None canvas = None canvas_image = None course_json = None scorecard = None inner_frame = None # Scorecard inner frame def drawPlaceholder(): global root global canvas global canvas_image default_im = ImageTk.PhotoImage(image=iim) canvas.itemconfig(canvas_image, image = default_im) root.update() def scorecardSumList(l): front = 0 back = 0 total = 0 output = [] for i in range(0, len(l)): value = math.ceil(l[i]) total += value if i < 9: front += value else: back += value output.append(value) if i == 8: output.append(front) if back > 0: output.append(back) output.append(total) return output def drawScorecard(course_json): global scorecard global inner_frame # Which Tkinter colors to draw on the chart hole_color = "snow" par_color = "bisque" pin_color = "gray40" tee_color_order = course_json["teeColours"] tee_colors_dict = { "red":"firebrick1", "white":"floral white", "blue":"navy", "black":"gray10", "green":"dark green", "gold":"gold" } tee_text_colors_dict = { "red":"black", "white":"black", "blue":"white", "black":"white", "green":"white", "gold":"black" } # Determine tee colors for chart tee_colors = [] tee_text_colors = [] for color in tee_color_order: color_name = tgc_definitions.tee_colors[color] tee_colors.append(tee_colors_dict[color_name]) tee_text_colors.append(tee_text_colors_dict[color_name]) # Clear the chart each time the course is redrawn if inner_frame is not None: inner_frame.destroy() inner_frame = Frame(scorecard, bg="darkgrey") inner_frame.pack(padx=10, pady=5, fill=tk.BOTH, expand=True) # Par and yardages values from holes pars, pin_counts, tees = tgc_tools.get_hole_information(course_json) # Determine number of valid tee sets # And fill in missing information for tees that weren't placed valid_tees = [] for tee in tees: empty_tee = all(v is None for v in tee) if not empty_tee: valid_tees.append(tee) for i in range(0, len(pars)): last_valid_distance = 0.0 for j in range(0, len(valid_tees)): if valid_tees[j][i] is not None: last_valid_distance = valid_tees[j][i] else: valid_tees[j][i] = last_valid_distance scorecard_pars = scorecardSumList(pars) scorecard_tees = [] for t in valid_tees: scorecard_tees.append(scorecardSumList(t)) # Generate top holes label and pin counts holes = [] pins = [] for i in range(0, len(pars)): holes.append(i+1) pins.append(pin_counts[i]) if i == 8: holes.append("out") pins.append("") if len(pars) > 9: holes.append("in") pins.append("") holes.append("tot") pins.append("") h = Entry(inner_frame, justify='center', fg="black", readonlybackground=hole_color, width=10) h.insert(0, "HOLE") h.configure(state="readonly") h.grid(row=0, column=0) h = Entry(inner_frame, justify='center', fg="black", readonlybackground=par_color, width=10) h.insert(0, "PAR") h.configure(state="readonly") h.grid(row=1, column=0) h = Entry(inner_frame, justify='center', fg="black", readonlybackground=pin_color, width=10) h.insert(0, "NUM PINS") h.configure(state="readonly") h.grid(row=2, column=0) for j in range(0, len(scorecard_tees)): h = Entry(inner_frame, justify='center', fg=tee_text_colors[j], readonlybackground=tee_colors[j], width=10) h.insert(0, "TEE SET") h.configure(state="readonly") h.grid(row=3+j, column=0) for i in range(0, len(holes)): # Append hole numbers h = Entry(inner_frame, justify='center', fg="black", readonlybackground=hole_color, width=5) h.insert(0, str(holes[i])) h.configure(state="readonly") h.grid(row=0, column=i+1) # Append par values p = Entry(inner_frame, justify='center', fg="black", readonlybackground=par_color, width=5) p.insert(0, str(scorecard_pars[i])) p.configure(state="readonly") p.grid(row=1, column=i+1) # Append number of pins - useful for working around simulator bug p = Entry(inner_frame, justify='center', fg="black", readonlybackground=pin_color, width=5) p.insert(0, str(pins[i])) p.configure(state="readonly") p.grid(row=2, column=i+1) # Append yardages for j in range(0, len(scorecard_tees)): p = Entry(inner_frame, justify='center', fg=tee_text_colors[j], readonlybackground=tee_colors[j], width=5) p.insert(0, str(scorecard_tees[j][i])) p.configure(state="readonly") p.grid(row=3+j, column=i+1) def drawCourse(cjson): global root global canvas global canvas_image data = drawCourseAsImage(cjson) im = Image.fromarray((255.0*data).astype(np.uint8), 'RGB').resize((image_width, image_height), Image.NEAREST) im = im.transpose(Image.FLIP_TOP_BOTTOM) cim = ImageTk.PhotoImage(image=im) canvas.img = cim # Need to save reference to ImageTK canvas.itemconfig(canvas_image, image = cim) drawScorecard(cjson) root.update() def getCourseDirectory(output): global root global course_json cdir = tk.filedialog.askdirectory(initialdir = ".", title = "Select course directory") if cdir: root.filename = cdir output.config(text=root.filename) drawPlaceholder() # Clear out existing course while picking a new one try: course_json = tgc_tools.get_course_json(root.filename) name_entry.configure(state='normal') course_name_var.set(course_json["name"]) if course_json is not None: drawCourse(course_json) except: pass def alert(msg): popup = tk.Toplevel() popup.geometry("200x200") popup.wm_title("Alert") label = ttk.Label(popup, text=msg, wraplength=200, justify=CENTER) label.pack(side="top", fill="x", pady=10) B1 = ttk.Button(popup, text="OK", command = popup.destroy) B1.pack() popup.mainloop() def disableAllChildren(var, frame): for child in frame.winfo_children(): if var.get(): # Check enabled child['state'] = 'normal' else: child['state'] = 'disable' # Also support children directly outside of a frame if len(frame.winfo_children()) == 0: if var.get(): # Check enabled frame['state'] = 'normal' else: frame['state'] = 'disable' def validateCourseName(action_type, index, value, previous, new_text, validation_types, validation_type, widget_name): # We also can submit as long of a course name as we want, so remove this limit! #if len(value) > 32: # return False return True # Looks like they accept almost all characters, wow... '''allowed_chars = string.ascii_letters + string.digits + ' ' allowed_set = set(allowed_chars) valid = all(x in allowed_set for x in new_text) if not valid: return False course_name_var.set(previous) return True''' course_types = [ ('Golf Course Files', '*.course'), ('All files', '*'), ] def importCourseAction(): global root global course_json if not root or not hasattr(root, 'filename'): alert("Select a course directory before importing a .course file") return input_course = tk.filedialog.askopenfilename(title='Course File', defaultextension='course', initialdir=root.filename, filetypes=course_types) if input_course: drawPlaceholder() tgc_tools.unpack_course_file(root.filename, input_course) course_json = tgc_tools.get_course_json(root.filename) name_entry.configure(state='normal') course_name_var.set(course_json["name"]) drawCourse(course_json) def exportCourseAction(): global root global course_json if not root or not hasattr(root, 'filename'): alert("Select a course directory before exporting a .course file") return if course_json is None: alert("Make sure to import a .course file") return dest_file = tk.filedialog.asksaveasfilename(title='Save Course As', defaultextension='.course', initialdir=root.filename, confirmoverwrite=True, filetypes=course_types) if dest_file: new_course_name = course_name_var.get() if len(new_course_name) > 0: course_json["name"] = new_course_name # Need to update the metadata file or it won't show up right in the course list tgc_tools.set_course_metadata_name(root.filename, course_json["name"]) drawPlaceholder() tgc_tools.pack_course_file(root.filename, None, dest_file, course_json) drawCourse(course_json) def autoPositionAction(): global course_json drawPlaceholder() course_json = tgc_tools.auto_position_course(course_json) drawCourse(course_json) def shiftAction(ew_entry, ns_entry, stype="course"): global course_json try: easting_shift = float(ew_entry.get()) northing_shift = float(ns_entry.get()) except: print("No action taken: Could not get valid shifts from entry") return drawPlaceholder() if stype == "course": course_json = tgc_tools.shift_course(course_json, easting_shift, northing_shift) elif stype == "terrain": course_json = tgc_tools.shift_terrain(course_json, easting_shift, northing_shift) elif stype == "features": course_json = tgc_tools.shift_features(course_json, easting_shift, northing_shift) else: print("No action taken: Unknown shift type: " + stype) drawCourse(course_json) def rotateAction(rotate_entry): global course_json try: rotation_degrees = float(rotate_entry.get()) rotation = rotation_degrees * math.pi / 180.0 except: print("No action taken: Could not get valid rotation from entry") return drawPlaceholder() course_json = tgc_tools.rotate_course(course_json, -rotation) drawCourse(course_json) def elevateAction(elevate_entry, auto=False): global course_json elevation_shift = None if not auto: try: elevation_shift = float(elevate_entry.get()) except: print("No action taken: Could not get valid elevation shift from entry") return if elevation_shift is not None and elevation_shift == 0.0: # Need to not pass in 0.0 or auto elevation shift will be triggered print("No action taken: shift requested was zero") return drawPlaceholder() course_json = tgc_tools.elevate_terrain(course_json, elevation_shift) drawCourse(course_json) numpy_types = [ ('Golf Course Features', '*.npy'), ('All files', '*'), ] numpy_terrain_types = [ ('Golf Course Terrain', '*.terrain.npy'), ('All files', '*'), ] numpy_holes_types = [ ('Golf Course Holes', '*.holes.npy'), ('All files', '*'), ] def separateAction(stype="terrain"): global course_json if stype == "terrain": name = "Terrain" f = tgc_tools.strip_terrain ext = ".terrain.npy" filetypes = numpy_terrain_types elif stype == "holes": name = "Holes" f = tgc_tools.strip_holes ext = ".holes.npy" filetypes = numpy_holes_types else: print("No action taken: Unknown separate type: " + stype) return dest_file = tk.filedialog.asksaveasfilename(title=name+' filename', defaultextension=ext, initialdir=root.filename, confirmoverwrite=True, filetypes=filetypes) if dest_file: drawPlaceholder() course_json = f(course_json, dest_file) drawCourse(course_json) def insertAction(stype="terrain"): global course_json if stype == "terrain": name = "Terrain" f = tgc_tools.insert_terrain ext = ".terrain.npy" filetypes = numpy_terrain_types elif stype == "holes": name = "Holes" f = tgc_tools.insert_holes ext = ".holes.npy" filetypes = numpy_holes_types else: print("No action taken: Unknown insert type: " + stype) return input_file = tk.filedialog.askopenfilename(title=name+' filename', defaultextension=ext, initialdir=root.filename, filetypes=filetypes) if input_file: drawPlaceholder() course_json = f(course_json, input_file) drawCourse(course_json) def confirmCourse(popup, new_course_json): global course_json popup.destroy() drawPlaceholder() if new_course_json is not None: course_json = new_course_json drawCourse(course_json) def combineAction(): global course_json other_course_dir = tk.filedialog.askdirectory(initialdir = ".", title = "Select second course directory") if other_course_dir: drawPlaceholder() course1_json = copy.deepcopy(course_json) # Make copy so this isn't "permanent" in memory course2_json = tgc_tools.get_course_json(other_course_dir) course1_json = tgc_tools.merge_courses(course1_json, course2_json) drawCourse(course1_json) popup = tk.Toplevel() popup.geometry("400x400") popup.wm_title("Confirm course merge?") label = ttk.Label(popup, text="Confirm course merge?") label.pack(side="top", fill="x", pady=10) B1 = ttk.Button(popup, text="Yes, Merge", command = partial(confirmCourse, popup, course1_json)) B1.pack() B2 = ttk.Button(popup, text="No, Abandon Merge", command = partial(confirmCourse, popup, None)) B2.pack() popup.mainloop() def rightClickMenu(e): try: def rClick_Copy(e, apnd=0): e.widget.event_generate('<Control-c>') def rClick_CopyAll(e, apnd=0): e.widget.event_generate('<Control-a>') e.widget.event_generate('<Control-c>') e.widget.focus() nclst=[('Copy', lambda e=e: rClick_Copy(e)), ('Copy All', lambda e=e: rClick_CopyAll(e))] rmenu = Menu(None, tearoff=0, takefocus=0) for (txt, cmd) in nclst: rmenu.add_command(label=txt, command=cmd) rmenu.tk_popup(e.x_root+40, e.y_root+10,entry="0") except TclError: pass return "break" def tkinterPrintFunction(root, textfield, message): textfield.configure(state='normal') textfield.insert(tk.END, message + "\n") textfield.configure(state='disabled') textfield.see(tk.END) textfield.bind('<Button-3>', rightClickMenu, add='') root.update() def runLidar(scale_entry, epsg_entry, printf): global root if not root or not hasattr(root, 'filename'): alert("Select a course directory before processing lidar files") return try: sample_scale = float(scale_entry.get()) except: alert("No action taken: Could not get valid resolution from entry") return force_epsg = None try: epsg_raw = epsg_entry.get() if epsg_raw: # Don't process empty string force_epsg = int(epsg_raw) except: alert("No action taken: Could not get valid force epsg from entry") return lidar_dir_path = tk.filedialog.askdirectory(initialdir=root.filename, title="Select las/laz files directory") if lidar_dir_path: lidar_map_api.generate_lidar_previews(lidar_dir_path, sample_scale, root.filename, force_epsg=force_epsg, printf=printf) def generateCourseFromLidar(options_entries_dict, printf): global root global course_json if not root or not hasattr(root, 'filename'): alert("Select a course directory before processing heightmap file") return if course_json is None: alert("Make sure to import a .course file") return # There may be many options for this in the future (which splines to add, clear splines?, flatten fairways/greens, etc) so store efficiently options_dict = {} # Snapshot the current values of the entries dictionary into the options_dict # We are reusing the same keys, so try not to change them often # All values in the entries_dict must support the get() function for key, entry in options_entries_dict.items(): options_dict[key] = entry.get() heightmap_dir_path = tk.filedialog.askdirectory(initialdir=root.filename, title="Select heightmap and mask files directory") if heightmap_dir_path: drawPlaceholder() course_json = tgc_image_terrain.generate_course(course_json, heightmap_dir_path, options_dict=options_dict, printf=printf) drawCourse(course_json) printf("Done Rendering Course Preview") osm_types = [ ('Open Street Map Exports', '*.osm'), ('All files', '*'), ] def importOSMFile(options_entries_dict, printf): global root global course_json if not root or not hasattr(root, 'filename'): alert("Select a course directory before importing OSM Flat Course") return if course_json is None: alert("Make sure to import a .course file") return # There may be many options for this in the future (which splines to add, clear splines?, flatten fairways/greens, etc) so store efficiently options_dict = {} # Snapshot the current values of the entries dictionary into the options_dict # We are reusing the same keys, so try not to change them often # All values in the entries_dict must support the get() function for key, entry in options_entries_dict.items(): options_dict[key] = entry.get() osm_file = tk.filedialog.askopenfilename(title='Select your OpenStreetMap Export', defaultextension='osm', initialdir=root.filename, filetypes=osm_types) if osm_file: with open(osm_file, encoding="utf8") as f: xml_data = f.read() printf("Loading OpenStreetMap Data from " + str(osm_file)) drawPlaceholder() course_json = tgc_image_terrain.generate_flat_course(course_json, xml_data, options_dict=options_dict, printf=printf) drawCourse(course_json) printf("Done Rendering Course Preview") root = tk.Tk() root.geometry("800x600") style = ttk.Style() style.theme_create( "TabStyle", parent="alt", settings={ "TNotebook": {"configure": {"tabmargins": [2, 5, 2, 0] } }, "TNotebook.Tab": {"configure": {"padding": [30, 5] },}}) style.theme_use("TabStyle") root.title("TGC Golf Tools " + TGC_GUI_VERSION) header_frame = Frame(root) output = Label(header_frame, background="lightgrey", width=75, height=1) output.pack(side=LEFT) B = Button(header_frame, text = "Select Course Directory", command = partial(getCourseDirectory, output)) B.pack(side=LEFT) header_frame.pack(fill=X) nb = ttk.Notebook(root) s = ttk.Style() s.configure('new.TFrame', background='#A9A9A9') tools = ttk.Frame(nb, style='new.TFrame') lidar = ttk.Frame(nb, style='new.TFrame') course = ttk.Frame(nb, style='new.TFrame') scorecard = ttk.Frame(nb, style='new.TFrame') nb.pack(fill=BOTH, expand=1) nb.add(tools, text='Course Tools') nb.add(lidar, text='Process Lidar') nb.add(course, text='Import Terrain and Features') nb.add(scorecard, text="Scorecard") ## Tools Tab image_frame = Frame(tools, width=image_width, height=image_height) image_frame.pack(anchor=NW, side=LEFT) canvas = tk.Canvas(image_frame, width=image_width, height=image_height) canvas.place(x=0,y=0) bg_color = "darkgrey" tool_bg = "grey25" text_fg = "grey90" tool_buttons_frame = Frame(tools, bg=bg_color) tool_buttons_frame.pack(side=LEFT, fill=BOTH, expand=1) ib = Button(tool_buttons_frame, text="Import .course", command=importCourseAction) ib.pack(pady=5) eb = Button(tool_buttons_frame, text="Export .course", command=exportCourseAction) eb.pack(pady=5) name_frame = Frame(tool_buttons_frame, bg=tool_bg) Label(name_frame, text="Name", fg=text_fg, bg=tool_bg).pack(side=LEFT, padx=5) course_name_var = tk.StringVar() name_entry = tk.Entry(name_frame,textvariable=course_name_var, width=32, justify='left', validate="key") name_entry['validatecommand'] = (name_entry.register(validateCourseName), '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W') name_entry.configure(state='disabled') name_entry.configure(disabledbackground="grey50") name_entry.pack(side=LEFT, padx=10, pady=5) name_frame.pack(pady=5) # Buttons that move things move_frame = Frame(tool_buttons_frame, bg=tool_bg) Label(move_frame, text="West->East", fg=text_fg, bg=tool_bg).grid(row=0, sticky=W, padx=5) Label(move_frame, text="South->North", fg=text_fg, bg=tool_bg).grid(row=1, sticky=W, padx=5) ew = tk.Entry(move_frame, width=10, justify='center') ew.insert(END, '0.0') ew.grid(row=0, column=1, padx=5) ns = tk.Entry(move_frame, width=10, justify='center') ns.insert(END, '0.0') ns.grid(row=1, column=1, padx=5) move_buttons_frame = Frame(move_frame, bg=tool_bg) mcb = Button(move_buttons_frame, text="Move Course", command=partial(shiftAction, ew, ns, "course")) mcb.pack(pady=5) mtb = Button(move_buttons_frame, text="Shift Terrain", command=partial(shiftAction, ew, ns, "terrain")) mtb.pack(pady=5) mfb = Button(move_buttons_frame, text="Shift Features", command=partial(shiftAction, ew, ns, "features")) mfb.pack(pady=5) move_buttons_frame.grid(row=0, column=2, rowspan=2, padx=10) move_frame.pack(pady=5) # Rotation with text field rotate_frame = Frame(tool_buttons_frame, bg=tool_bg) Label(rotate_frame, text="Rotation (Degrees)", fg=text_fg, bg=tool_bg).grid(row=0, sticky=W, padx=5) er = tk.Entry(rotate_frame, width=8, justify='center') er.insert(END, '0.0') er.grid(row=0, column=1, padx=5) rb = Button(rotate_frame, text="Rotate", command=partial(rotateAction, er)) rb.grid(row=0, column=2, padx=10, pady=5) rotate_frame.pack(pady=5) # Elevation shift aeb = Button(tool_buttons_frame, text="Auto Shift Elevations", command=partial(elevateAction, None, True)) aeb.pack(pady=5) elevate_frame = Frame(tool_buttons_frame, bg=tool_bg) Label(elevate_frame, text="Down->Up", fg=text_fg, bg=tool_bg).grid(row=0, sticky=W, padx=5) ee = tk.Entry(elevate_frame, width=8, justify='center') ee.insert(END, '0.0') ee.grid(row=0, column=1, padx=5) etb = Button(elevate_frame, text="Shift Elevations", command=partial(elevateAction, ee)) etb.grid(row=0, column=2, padx=10, pady=5) elevate_frame.pack(pady=5) terrain_frame = Frame(tool_buttons_frame, bg=bg_color) stb = Button(terrain_frame, text="Separate Terrain", command=partial(separateAction, "terrain")) stb.pack(side=LEFT, padx=5, pady=5) itb = Button(terrain_frame, text="Insert Terrain File", command=partial(insertAction, "terrain")) itb.pack(side=LEFT, pady=5) terrain_frame.pack() holes_frame = Frame(tool_buttons_frame, bg=bg_color) shb = Button(holes_frame, text="Separate Holes", command=partial(separateAction, "holes")) shb.pack(side=LEFT, padx=5, pady=5) ihb = Button(holes_frame, text="Insert Holes File", command=partial(insertAction, "holes")) ihb.pack(side=LEFT, pady=5) holes_frame.pack() ccb = Button(tool_buttons_frame, text="Combine Course Directory", command=combineAction) ccb.pack(pady=5) default_im = ImageTk.PhotoImage(image=iim) canvas_image = canvas.create_image(0,0,image=default_im,anchor=tk.NW) root.update() ## Lidar Tab lidarConsoleOutput = tk.scrolledtext.ScrolledText(master=lidar, wrap=tk.WORD, width=20, height=10, state=DISABLED) lidarPrintf = partial(tkinterPrintFunction, lidar, lidarConsoleOutput) lidarControlFrame = Frame(lidar, bg=tool_bg) scale_label = Label(lidarControlFrame, text="Map Scale", fg=text_fg, bg=tool_bg) scale_entry = tk.Entry(lidarControlFrame, width=8, justify='center') scale_entry.insert(END, 2.0) epsg_label = Label(lidarControlFrame, text="Force Lidar EPSG Projection", fg=text_fg, bg=tool_bg) epsg_entry = tk.Entry(lidarControlFrame, width=8, justify='center') epsg_entry.insert(END, "") lidarbutton = Button(lidarControlFrame, text="Select Lidar and Generate Heightmap", command=partial(runLidar, scale_entry, epsg_entry, lidarPrintf)) scale_label.pack(side=LEFT, padx=5) scale_entry.pack(side=LEFT, padx=5) epsg_label.pack(side=LEFT, padx=5) epsg_entry.pack(side=LEFT, padx=5) lidarbutton.pack(side=LEFT, padx=5, pady=5) lidarControlFrame.pack(pady=5) lidarConsoleOutput.pack(padx=10, pady=5, fill=tk.BOTH, expand=True) ## Import Terrain and Features Tab courseConsoleOutput = tk.scrolledtext.ScrolledText(master=course, wrap=tk.WORD, width=20, height=10, state=DISABLED) coursePrintf = partial(tkinterPrintFunction, course, courseConsoleOutput) courseControlFrame = Frame(course, bg=bg_color) options_entries_dict = {} # Store the many entries into one dictionary # OpenStreetMap Options check_fg = "black" # Check fg can't be light or near white or it is invisible in the checkbox check_bg = "grey80" osmControlFrame = Frame(courseControlFrame, bg=tool_bg) osmSubFrame = Frame(osmControlFrame, bg=check_bg) # Can disable all OSM Options easily inside of this options_entries_dict["use_osm"] = tk.BooleanVar() useOSMCheck = Checkbutton(osmControlFrame, text="Import OpenStreetMap", variable=options_entries_dict["use_osm"], fg=check_fg, bg="grey60") useOSMCheck['command'] = partial(disableAllChildren, options_entries_dict["use_osm"], osmSubFrame) useOSMCheck.select() # Default to Checked Label(osmSubFrame, text="Fine Shift West->East", fg=check_fg, bg=check_bg).grid(row=0, sticky=W, padx=5) Label(osmSubFrame, text="Fine Shift South->North", fg=check_fg, bg=check_bg).grid(row=1, sticky=W, padx=5) osmew = tk.Entry(osmSubFrame, width=10, justify='center') osmew.insert(END, '0.0') options_entries_dict["adjust_ew"] = osmew osmns = tk.Entry(osmSubFrame, width=10, justify='center') osmns.insert(END, '0.0') options_entries_dict["adjust_ns"] = osmns options_entries_dict["bunker"] = tk.BooleanVar() bunkerCheck = Checkbutton(osmSubFrame, text="Import Bunkers", variable=options_entries_dict["bunker"], fg=check_fg, bg=check_bg) bunkerCheck.select() options_entries_dict["green"] = tk.BooleanVar() greenCheck = Checkbutton(osmSubFrame, text="Import Greens", variable=options_entries_dict["green"], fg=check_fg, bg=check_bg) greenCheck.select() options_entries_dict["fairway"] = tk.BooleanVar() fairwayCheck = Checkbutton(osmSubFrame, text="Import Fairways", variable=options_entries_dict["fairway"], fg=check_fg, bg=check_bg) fairwayCheck.select() options_entries_dict["range"] = tk.BooleanVar() rangeCheck = Checkbutton(osmSubFrame, text="Import Driving Ranges", variable=options_entries_dict["range"], fg=check_fg, bg=check_bg) rangeCheck.select() options_entries_dict["teebox"] = tk.BooleanVar() teeboxCheck = Checkbutton(osmSubFrame, text="Import Teeboxes", variable=options_entries_dict["teebox"], fg=check_fg, bg=check_bg) teeboxCheck.select() options_entries_dict["rough"] = tk.BooleanVar() roughCheck = Checkbutton(osmSubFrame, text="Import Rough", variable=options_entries_dict["rough"], fg=check_fg, bg=check_bg) roughCheck.select() options_entries_dict["water"] = tk.BooleanVar() waterCheck = Checkbutton(osmSubFrame, text="Import Water", variable=options_entries_dict["water"], fg=check_fg, bg=check_bg) waterCheck.select() options_entries_dict["cartpath"] = tk.BooleanVar() cartpathCheck = Checkbutton(osmSubFrame, text="Import Cartpaths", variable=options_entries_dict["cartpath"], fg=check_fg, bg=check_bg) cartpathCheck.select() options_entries_dict["path"] = tk.BooleanVar() pathCheck = Checkbutton(osmSubFrame, text="Import Walking Paths", variable=options_entries_dict["path"], fg=check_fg, bg=check_bg) pathCheck.select() options_entries_dict["hole"] = tk.BooleanVar() holeCheck = Checkbutton(osmSubFrame, text="Import Holes", variable=options_entries_dict["hole"], fg=check_fg, bg=check_bg) holeCheck.select() osm_hole_filter = tk.Entry(osmSubFrame, width=10, justify='center') options_entries_dict["hole_name_filter"] = osm_hole_filter options_entries_dict["building"] = tk.BooleanVar() buildingCheck = Checkbutton(osmSubFrame, text="Import Buildings", variable=options_entries_dict["building"], fg=check_fg, bg=check_bg) buildingCheck.select() options_entries_dict["tree"] = tk.BooleanVar() treeCheck = Checkbutton(osmSubFrame, text="Import Mapped Woods/Trees", variable=options_entries_dict["tree"], fg=check_fg, bg=check_bg) treeCheck.deselect() osmbutton = Button(osmSubFrame, text="Make Flat Course From OSM File", command=partial(importOSMFile, options_entries_dict, coursePrintf)) osmew.grid(row=0, column=1, padx=5) osmns.grid(row=1, column=1, padx=5) bunkerCheck.grid(row=2, columnspan=2, sticky=W, padx=5) greenCheck.grid(row=3, columnspan=2, sticky=W, padx=5) fairwayCheck.grid(row=4, columnspan=2, sticky=W, padx=5) rangeCheck.grid(row=5, columnspan=2, sticky=W, padx=5) teeboxCheck.grid(row=6, columnspan=2, sticky=W, padx=5) roughCheck.grid(row=7, columnspan=2, sticky=W, padx=5) waterCheck.grid(row=8, columnspan=2, sticky=W, padx=5) cartpathCheck.grid(row=9, columnspan=2, sticky=W, padx=5) pathCheck.grid(row=10, columnspan=2, sticky=W, padx=5) holeCheck.grid(row=11, columnspan=2, sticky=W, padx=5) Label(osmSubFrame, text="Match Hole Names", fg=check_fg, bg=check_bg).grid(row=12, sticky=W, padx=5) osm_hole_filter.grid(row=12, column=1, padx=5) buildingCheck.grid(row=13, columnspan=2, sticky=W, padx=5) treeCheck.grid(row=14, columnspan=2, sticky=W, padx=5) osmbutton.grid(row=15, columnspan=2) useOSMCheck.pack(padx=10, pady=10) osmSubFrame.pack(padx=5, pady=5) coursebutton = Button(courseControlFrame, text="Select and Import Heightmap and OSM into Course", command=partial(generateCourseFromLidar, options_entries_dict, coursePrintf)) # Pack the controls frames, button at the top followed by the options coursebutton.pack(padx=10, pady=10) # Other Course Options courseOptionsFrame = Frame(courseControlFrame, bg=tool_bg) courseSubFrame = Frame(courseOptionsFrame, bg=check_bg) # Not needed for anything here, but I like the look Label(courseOptionsFrame, text='Course Options', fg=text_fg, bg=tool_bg).pack(pady=(15,10)) options_entries_dict["add_background"] = tk.BooleanVar() backgroundCheck = Checkbutton(courseSubFrame, text="Add Background Terrain/Remove Cliffs", variable=options_entries_dict["add_background"], fg=check_fg, bg=check_bg) backgroundCheck.deselect() bgentry = tk.Entry(courseSubFrame, width=10, justify='center') bgentry.insert(END, '16.0') options_entries_dict["background_scale"] = bgentry backgroundCheck['command'] = partial(disableAllChildren, options_entries_dict["add_background"], bgentry) options_entries_dict["lidar_trees"] = tk.BooleanVar() lidarTreeCheck = Checkbutton(courseSubFrame, text="Add Trees From Lidar (Experimental)", variable=options_entries_dict["lidar_trees"], fg=check_fg, bg=check_bg) lidarTreeCheck.deselect() options_entries_dict["tree_variety"] = tk.BooleanVar() treeVarietyCheck = Checkbutton(courseSubFrame, text="Tree Variety (Lidar and OSM)", variable=options_entries_dict["tree_variety"], fg=check_fg, bg=check_bg) treeVarietyCheck.deselect() options_entries_dict["fill_water"] = tk.BooleanVar() fillWaterCheck = Checkbutton(courseSubFrame, text="Fill Holes Under Blue Mask", variable=options_entries_dict["fill_water"], fg=check_fg, bg=check_bg) fillWaterCheck.deselect() options_entries_dict["purge_water"] = tk.BooleanVar() purgeWaterCheck = Checkbutton(courseSubFrame, text="Remove All Terrain Under Blue Mask", variable=options_entries_dict["purge_water"], fg=check_fg, bg=check_bg) purgeWaterCheck.deselect() # Pack the osmControlFrame courseSubFrame.pack(padx=5, pady=5, fill=X, expand=True) backgroundCheck.grid(row=0, columnspan=2, sticky=W, padx=5) Label(courseSubFrame, text="Background Scale", fg=check_fg, bg=check_bg).grid(row=1, column=0, sticky=W, padx=5) bgentry.grid(row=1, column=1, sticky=W, padx=5) lidarTreeCheck.grid(row=2, columnspan=2, sticky=W, padx=5) treeVarietyCheck.grid(row=3, columnspan=2, sticky=W, padx=5) fillWaterCheck.grid(row=4, columnspan=2, sticky=W, padx=5) purgeWaterCheck.grid(row=5, columnspan=2, sticky=W, padx=5) # Pack the two option frames side by side osmControlFrame.pack(side=LEFT, anchor=N, padx=5) courseOptionsFrame.pack(side=LEFT, anchor=N, padx=5, fill=X, expand=True) # Pack the big frames side by side courseControlFrame.pack(side=LEFT, padx=5, pady=5, fill=tk.BOTH, expand=True) courseConsoleOutput.pack(side=LEFT, padx=5, pady=5, fill=tk.BOTH, expand=True) root.mainloop()