def _end_node_drag(self, coords): """ Performs actions to complete a node drag operation Validates node location, and other associated object information and updates the cache when a node drag is completed :coords: The coordinates associated with this event """ if self._cache["item"] is None: return # Obtain the final points x = coords[0] y = coords[1] item = self._cache["item"] self._validate_node_position(coords) container = self._manager.request(DataStore.DATATYPE.NODE, item) container.x_coordinate = x container.y_coordinate = y self._manager.inform(DataStore.EVENT.NODE_EDIT, container.empty_container(), self._cache["item"]) Debug.printi("Node " + str(self._cache["item"]) + " has been moved", Debug.Level.INFO) # Clean the cache self._clear_cache(coords)
def _add_new_wall_pic(self, pic=None): """ Adds a new wall picture to the node definition Launches a dialog, and validates and posts the entered information to the repository for the creation of a new wall picture for the node that is being edited """ # Display the dialogue results = pic if results is None: results = NodePictureDialog(self) results = results._entries item_id = results["name"] item = results # Extract the return values try: self.add_new(item, item_id) except DuplicateListHeapItemException: Debug.printi("Unable to add duplicate picture", Debug.Level.ERROR) return except MaxItemLimitReachedException: Debug.printi("Maximum number of pictures for this room reached", Debug.Level.ERROR) return
def _handle_mot(self, m_event, event): """ Callback function to handle movement of the mouse Function updates the mouse location status bar as well as setting cache values to the current location of the mouse :m_event: The specifier for the type of event that has been generated :event: The tk provided event object """ event.x = int(self._canvas.canvasx(event.x)) event.y = int(self._canvas.canvasy(event.y)) self._status.set_text("Mouse X:" + str(event.x) + "\tMouse Y:" + str(event.y)) item = self._get_current_item((event.x, event.y)) if self._is_node(item): Debug.printi("Node: " + str(item), Debug.Level.INFO) if self._is_edge(item): d_x = self._edge_bindings[item].x_start - self._edge_bindings[item].x_end d_y = self._edge_bindings[item].y_start - self._edge_bindings[item].y_end square = (d_x * d_x) + (d_y * d_y) distance = int(math.sqrt(square)) Debug.printi("Edge: " + str(item) + " | Source: " + str(self._edge_bindings[item].item_start) + " | Target: " + str(self._edge_bindings[item].item_end) + " | Length: " + str(distance)) self._cache["x"] = event.x self._cache["y"] = event.y
def _selection_operation(self, coords): """ Contextually create or edit a node :param coords: :return: """ # Determine the item ID item = self._get_current_item(coords) self._cache["item"] = item true_coords = self._canvas_to_screen((self._cache["x"], self._cache["y"])) if self._is_node(item): Debug.printi("Node Selected : " + str(item) + " | Launching Editor", Debug.Level.INFO) # Make request from object manager using the tag assigned populator = self._manager.request(DataStore.DATATYPE.NODE, item) updated_node = NodeDialog(self, true_coords[0] + 10, true_coords[1] + 10, populator=populator) # post information to object manager, or let the dialog handle it, or whatever self._manager.inform(DataStore.EVENT.NODE_EDIT, updated_node._entries, item) return if self._is_edge(item): Debug.printi("Edge Selected : " + str(item) + " | Launching Editor", Debug.Level.INFO) # Make a request from the object manager to populate the dialog populator = self._manager.request(DataStore.DATATYPE.EDGE, item) updated_edge = EdgeDialog(self, true_coords[0] + 10, true_coords[1] + 10, populator=populator) # Make sure that information is posted to the object manager self._manager.inform(DataStore.EVENT.EDGE_EDIT, updated_edge._entries, item) return if self._is_object(item): self._edit_object(coords) return
def _load_control_file(file_path): try: control_file = open(file_path) Debug.printi("Control file " + file_path + " successfully loaded", Debug.Level.INFO) return control_file except IOError as e: Debug.printi(e.message, Debug.Level.FATAL)
def _delete_object(self, item): if item not in self._object_listing: Debug.printi("Object does not exist to delete", Debug.Level.ERROR) return del self._object_listing[item] self._manager.inform(DataStore.EVENT.OBJECT_DELETE, data_id=item) self._canvas.itemconfig(item, outline="red", fill="black", activeoutline="black", activefill="red")
def validate(event, data): if DEBUG: return True, "" try: return DataValidator().VALIDATE_MAP[event](data) except KeyError as error: Debug.printi(error.message, Debug.Level.FATAL)
def _display_img(self): """ Display a loaded image in a dialog """ if self._file_path is None: Debug.printi("No picture has been loaded to preview", Debug.Level.ERROR) return photo = self._open_img(self._file_path) ImageViewDialog(self._parent, self._file_name, photo)
def _toggle_windowed(self): """ Toggle the windowed flag :return: """ self._win_var.set(0 if self._win_var.get() == 1 else 1) val = self._entries["windowed"] self._entries["windowed"] = not val Debug.printi("Windowing toggled to " + (str(not val)), Debug.Level.INFO) self._windowed.toggle()
def _toggle_distortion(self): """ Toggle the distortion flag :return: """ self._distortion_var.set(0 if self._distortion_var.get() == 1 else 1) val = self._entries["distortion"] self._entries["distortion"] = not val Debug.printi("Distortion toggled to " + (str(not val)), Debug.Level.INFO) self._distortion.toggle()
def _open_img(self, img_name): """ Open an image from its location on disk Retrieves an image in ImageTk form from a given file path and loads it for application use :img_name: The path/name (?) of the image to open """ try: img = Image.open(img_name) photo = ImageTk.PhotoImage(img) return photo except IOError: Debug.printi("Unable to find image " + img_name, Debug.Level.ERROR)
def dump_file(self): # Validate, abort if invalid result, message = self._subject.export_finalize() if result is False: # throw error, return tkMessageBox.showerror("Invalid Data", message) return # Obtain the file handle to print to handle = tkFileDialog.asksaveasfile(mode='w', defaultextension=".xml") if handle is None: # asksaveasfile return `None` if dialog closed with "cancel". return handle.write(self._xml_container.to_string()) handle.close() # `()` was missing. Debug.printi("File " + handle.name + " has been saved")
def _move_img(self): if self._auto_move is False: return # Else, move the image to the given folder that is in the same dir as this module try: src = self._file_path dest = os.path.dirname(os.path.realpath(__file__)) +"/" + self._file_name shutil.copy(src, dest) Debug.printi("Moving file " + self._file_path + " to location " + os.path.dirname(os.path.realpath(__file__)) + self._file_name, Debug.Level.INFO) # eg. src and dest are the same file except shutil.Error as e: print('Error: %s' % e + " " +dest) # eg. source or destination doesn't exist except IOError as e: print('Error: %s' % e.strerror +" "+ dest)
def _load_mesh(self): """ Open a file dialog to load a mesh filepath :return: """ Debug.printi("Load Mesh called", Debug.Level.INFO) types = \ [ ("DirectX", "*.x") ] dialog = tkFileDialog.Open(self, filetypes=types) file_path = dialog.show() file_path = self._move_img(file_path) self._mesh.delete(0, END) self._mesh.insert(0, str(file_path)) Debug.printi("Mesh Filepath:" + file_path, Debug.Level.INFO)
def update(self): Debug.printi("The state of the datastore has been updated", Debug.Level.INFO) # Retrieve the update details from the datastore, and then dispatch the changes to # the XML container update_event = (self._subject._cache["EVENT"], self._subject._cache["ID"], self._subject._cache["DATA"]) if update_event[0] == "Delete All": self._xml_container.create_skeleton(self._subject) return self._dispatch[update_event[0]]( self._extract_xml_id(update_event[0], update_event[2]), update_event[2] ) self._text_area.config(state=NORMAL) self._text_area.delete(1.0, END) self._text_area.insert(END, self._xml_container.to_string()) self._text_area.config(state=DISABLED)
def apply(self): mappings = {} for name in self._class_names: mappings[name] = [] for key, item in self._entries.iteritems(): mappings[key[0]].append(str(key[1]) + "=" + ((str(item.get())) if "False" in item.get() or "True" in item.get() else "\"" + str(item.get()) + "\"")) Debug.printi("Writing new default configurations") try: defaults_file = open("Defaults.py", "w") for key, item in mappings.iteritems(): defaults_file.write( "class " +key + ":" + "\n" + "".join(["\t" + str(set_p) + "\n" for set_p in item]) ) defaults_file.close() except IOError as e: Debug.printi(str(e) + "\n Please use backup file to restore Defaults.py", Debug.Level.FATAL) tkMessageBox.showerror("FATAL ERROR", "A fatal error has occured, \n Please use backup file to restore Defaults.py") else: tkMessageBox.showwarning("Restart", "Please restart the application for changes to take effect")
def _mark_start_node(self, node_id): """ Mark the passed in node as the starting node :param node_id: :return: """ # Print the debug information # Mark as the new starting node on the canvas, first check that it is a node if node_id in self._node_listing: Debug.printi("Node:" + str(node_id) + " has been marked as the new starting node", Debug.Level.INFO) if self._curr_start is not None: # Return the old starting node to its normal colour self._canvas.itemconfig(self._curr_start, outline="red", fill="black", activeoutline="black", activefill="red") self._curr_start = node_id self._canvas.itemconfig(node_id, outline="black", fill="green", activeoutline="green", activefill="black") # Inform the object manager that there is a new starting node environment_container = self._manager.request(DataStore.DATATYPE.ENVIRONMENT) environment_container.start_node = node_id self._manager.inform(DataStore.EVENT.ENVIRONMENT_EDIT, environment_container)
def _handle_mouse_events(self, m_event, event): """ Function that routes mouse events to the appropriate handlers Prints logging and UI information about the state of the mouse and then routes the mouse event to the appropriate handler :m_event: The specifier for the tupe of event that has been generated :event: The tk provided event object """ event.x = int(self._canvas.canvasx(event.x)) event.y = int(self._canvas.canvasy(event.y)) self._status.set_text("Mouse X:" + str(self._cache["x"]) + "\tMouse Y:" + str(self._cache["y"])) Debug.printet(event, m_event, Debug.Level.INFO) self._cache["event"] = event try: self._commands[m_event]((event.x, event.y)) except KeyError: Debug.printi("Warning, no control mapped to " + m_event, Debug.Level.ERROR) self._command_cache = m_event
def _generate_control_map(file): """ Generate a control map that can be parsed :param file: The file to parse the controls :return: """ try: line_list = file.readlines() con_map = [] for line in line_list: str = line.split(":") str[0] = ''.join(str[0].strip()) str[1] = ''.join(str[1].strip()) con_map.append([str[0], str[1]]) except (IOError, EOFError) as e: file.close() Debug.printi(e, Debug.Level.FATAL) else: file.close() Debug.printi("Control file successfully parsed", Debug.Level.INFO) return con_map
def _execute_edge(self, coords): """ Perform the operations that occur during the motion of an edge drag :param coords: :return: """ # Update the line position # We will update the line position by deleting and redrawing if not self._valid_edge_cache(): return self._canvas.delete(self._edge_cache["edge"]) self._edge_cache["edge"] = self._canvas.create_line( \ self._edge_cache["x_start"], self._edge_cache["y_start"], coords[0]-1, coords[1]-1, tags="edge", activefill="RoyalBlue1", tag="edge") d_x = self._edge_cache["x_start"] - coords[0] d_y = self._edge_cache["y_start"] - coords[1] square = (d_x * d_x) + (d_y * d_y) distance = math.sqrt(square) Debug.printi("Current corridor distance: " + str(int(distance)))
def _add_new_texture(self, tex=None): """ Adds a new wall picture to the node definition Launches a dialog, and validates and posts the entered information to the repository for the creation of a new wall picture for the node that is being edited """ # Display the dialogue results = tex if results is None: results = WallTextureDialog(self) results = results._entries item_id = results["path"] item = results # Extract the return values try: self.add_new(item, item_id) except DuplicateListHeapItemException: Debug.printi("Unable to add duplicate texture", Debug.Level.ERROR) return
def _move_img(self, file_path): """ Move the DirectX file to the Data folder automagically :param file_path: The file path of the file to be moved :return: """ try: src = file_path file_name = self._scrub_name(file_path) dest = os.path.dirname(os.path.realpath(__file__)) + "/" + file_name shutil.copy(src, dest) Debug.printi("Moving file " + file_path + " to location " + os.path.dirname(os.path.realpath(__file__)) + "/" + file_name, Debug.Level.INFO) return file_name # eg. src and dest are the same file except shutil.Error as e: print('Error: %s' % e + " " + dest) return file_name # eg. source or destination doesn't exist except IOError as e: print('Error: %s' % e.strerror + " " + dest) return file_name
def __init__(self, parent, title="MazeBuilder Dialog", lock_focus=True, x=None, y=None, populator=None, manager=None): """ Construct the instance of the dialog :parent: The parent widget that spawns the dialog :title: The title of the dialog that will be used in the header bar :lock_focus: Bind the focus of the mouse to this window until it is dismissed :x: The x coord to launch the dialog at :y: The y coords to launch the dialog at :populator: THe population parameters for the dialog :type manager: DataStore """ Toplevel.__init__(self, parent) self.title(title) self.transient(parent) self._manager = manager if title: self._title = title self.parent = parent self.result = None body = Frame(self) if populator is not None: self.populate(populator) self.initial_focus = self.body(body) body.pack(padx=5, pady=5) self.buttonbox() # Needed to grab the window on Linux machines while True: try: self.grab_set() except TclError: continue else: break if not self.initial_focus: self.initial_focus = self self.protocol("WM_DELETE_WINDOW", lambda: Debug.printi("Window Close Disabled", Debug.Level.ERROR)) if x is None and y is None: self.geometry("+%d+%d" % (parent.winfo_rootx() + 50, parent.winfo_rooty() + 50)) else: self.geometry("+%d+%d" % (x + 1, y + 1)) self.initial_focus.focus_set() if lock_focus: self.wait_window(self)
def _edit_object(self, coords): """ Awkward moment when you find a threading related bug in the Tkinter library, caused by some Tcl issue or something like that. The below line must be left commented out otherwise the window_wait call in the dialog will crash out with a Tcl ponter based issue :/ item = self._get_current_item((self._cache["x"], self._cache["y"])) This means that we can only use the mouse to edit objects """ item = self._get_current_item(coords) if item not in self._object_listing: Debug.printi("Not a valid object to edit", Debug.Level.ERROR) return obj = ObjectDialog(self, coords[0] + 10, coords[1] + 10, populator=self._manager.request(DataStore.DATATYPE.OBJECT, item)) Debug.printi("Editing object " + str(item), Debug.Level.INFO) self._manager.inform(DataStore.EVENT.OBJECT_EDIT, obj._entries, item) Debug.printi("Editing object " + str(item), Debug.Level.INFO)
def _mark_object(self, coords, prog=False, data=None): """ Mark a node as containing an object :param coords: :return: """ # Retrieve the item item = self._get_current_item(coords) if not prog: if item not in self._node_listing: Debug.printi("Invalid object placement selection", Debug.Level.ERROR) return if item in self._object_listing: Debug.printi("This room already has an object in it", Debug.Level.ERROR) return # Retrieve its coordinates # Launch the object maker dialog obj = ObjectDialog(self, coords[0] + 10, coords[1] + 10, populator=Containers.ObjectContainer(key_val={ "x_coordinate" : coords[0], "y_coordinate" : coords[1], "name" : "Object_"+str(item), "mesh" : None, "scale" : None })) entries = obj._entries else: entries = { "x_coordinate": coords[0], "y_coordinate": coords[1], "name": data["name"], "mesh": data["mesh"], "scale": data["scale"] } # Save informatoin to the manager self._manager.inform(DataStore.EVENT.OBJECT_CREATE, entries, item) self._object_listing[item] = item self._canvas.itemconfig(item, fill="blue") Debug.printi("Object created in room " + str(item), Debug.Level.INFO)
def _toggle_visibility(self): Debug.printi("Visibility changed to " + str(bool(self._visi_var)), Debug.Level.INFO) self._entries["visible"] = True
def auto_populate(self): """ Autopopulate the fields in the dialog This function is activated with the spacebar when in a dialog """ Debug.printi("Call to autopopulater registered")
def _end_edge(self, coords, prog=False, data=None): """ Perform the operations required to complete an edge creation operation :param coords: :return: """ # Check if the cursor is over a node, if so continue, else abort curr = self._get_current_item((coords[0], coords[1])) if not prog: if curr is None or not self._valid_edge_cache() or curr not in self._node_listing: # Abort the edge creation process self._canvas.delete(self._edge_cache["edge"]) self._clear_edge_cache() return # Check if this edge already exists in the program if self._check_duplicate_edges(self._edge_cache["item_start"], curr): self.delete_edge(self._edge_cache["edge"]) Debug.printi("Multiple edges between rooms not permitted", Debug.Level.ERROR) return #Ensure that edges arent made between the same room if curr == self._edge_cache["item_start"]: Debug.printi("Cannot allow paths starting and ending in the same room", Debug.Level.ERROR) return self._canvas.tag_lower("edge") self._edge_cache["item_end"] = curr # Note that we use the edge ID as the key self._edge_bindings[self._edge_cache["edge"]] = EdgeBind(self._edge_cache) self._edge_bindings[self._edge_cache["edge"]].x_end = coords[0] self._edge_bindings[self._edge_cache["edge"]].y_end = coords[1] # Inform the manager if not prog: self._manager.inform( DataStore.EVENT.EDGE_CREATE, { "source" : self._edge_cache["item_start"], "target" : self._edge_cache["item_end"], "height" : None, "wall1" : { "height":Defaults.Edge.WALL_HEIGHT, "textures":{ Defaults.Wall.PATH: { "path":Defaults.Wall.PATH, "tile_x":Defaults.Wall.TILE_X, "tile_y":Defaults.Wall.TILE_Y, "height":None } } } if Defaults.Config.EASY_MAZE else None, "wall2" : { "height": Defaults.Edge.WALL_HEIGHT, "textures": { Defaults.Wall.PATH: { "path": Defaults.Wall.PATH, "tile_x": Defaults.Wall.TILE_X, "tile_y": Defaults.Wall.TILE_Y, "height":None } } } } if Defaults.Config.EASY_MAZE else None, self._edge_cache["edge"]) else: # We are programmatically adding the edges in self._manager.inform( DataStore.EVENT.EDGE_CREATE, { "source": self._edge_cache["item_start"], "target": self._edge_cache["item_end"], "height": None, "wall1": data["wall1"], "wall2": data["wall2"] }, self._edge_cache["edge"]) Debug.printi("Edge created between rooms " + str(self._edge_cache["item_start"]) + " and " + str(self._edge_cache["item_end"]) , Debug.Level.INFO) self._clear_edge_cache() self._clear_cache(coords)
def import_maze(self, filepath, canvas, datastore): """ Imports a maze from an XML file This will overwrite any current information, you will lose the current work :param filepath: :return: """ inputter = XMlInputer() self if filepath is '': Debug.printi("No input file to read", Debug.Level.INFO) return inputter.read_file(filepath) Debug.printi("Maze input file " + filepath + " has been read", Debug.Level.INFO) self._all_entries.clear() for node in inputter._root.iter("node"): original_id = node.attrib["id"] node.attrib["id"] = "n_" + node.attrib["id"] for edge in inputter._root.iter("edge"): if edge.attrib["source"] == original_id: edge.attrib["source"] = node.attrib["id"] if edge.attrib["target"] == original_id: edge.attrib["target"] = node.attrib["id"] i = 0 for node in inputter._root.iter("node"): # Use an index to assign the id to the nodes i += 1 self._all_entries[str(i)] = node attributes = node.attrib old_id = attributes["id"] attributes["id"] = str(i) pictures = {} for pic in node.getchildren(): pictures[pic.attrib["name"]] = pic.attrib canvas.create_new_node( ( int(attributes["x"]), int(attributes["y"]) ), prog=True, data=(attributes, pictures) ) Debug.printi("New Node Created from file ID:" + node.attrib["id"], Debug.Level.INFO) # Since we are changing the id's we need to update all of the edges that use them for edge in inputter._root.iter("edge"): if edge.attrib["source"] == old_id: edge.attrib["source"] = str(i) if edge.attrib["target"] == old_id: edge.attrib["target"] = str(i) for edge in inputter._root.iter("edge"): self._all_entries[(edge.attrib["source"], edge.attrib["target"])] = edge source_coords = int(self._all_entries[edge.attrib["source"]].attrib["x"]), \ int(self._all_entries[edge.attrib["source"]].attrib["y"]) target_coords = int(self._all_entries[edge.attrib["target"]].attrib["x"]), \ int(self._all_entries[edge.attrib["target"]].attrib["y"]) wall1_node = edge.find("Wall1") wall2_node = edge.find("Wall2") wall1 = { "height" : wall1_node.attrib["height"] if len(wall1_node.attrib) > 0 else None, "textures" : {} } wall2 = { "height": wall2_node.attrib["height"] if len(wall2_node.attrib) > 0 else None, "textures": {} } for texture in wall1_node.iter("Texture"): wall1["textures"]["path"] = { "path" : texture.attrib["path"], "tile_x": texture.attrib["tileX"], "tile_y": texture.attrib["tileY"] } for texture in wall2_node.iter("Texture"): wall2["textures"]["path"] = { "path": texture.attrib["path"], "tile_x": texture.attrib["tileX"], "tile_y": texture.attrib["tileY"] } canvas._clear_cache(source_coords) canvas._begin_edge(source_coords) canvas._execute_edge(target_coords) canvas._end_edge(target_coords, prog=True, data={ "source": edge.attrib["source"], "target": edge.attrib["target"], "height": None, "wall1": wall1, "wall2": wall2 }) Debug.printi("New EDGE Created from file Source:" + edge.attrib["source"] + " Target: " + edge.attrib["target"], Debug.Level.INFO) for object in inputter._root.iter("object"): self._all_entries[object.attrib["name"]] = object canvas._mark_object((int(object.attrib["x"]), int(object.attrib["y"])), prog=True, data={ "x_coordinate": object.attrib["x"], "y_coordinate": object.attrib["y"], "name": object.attrib["name"], "mesh": object.attrib["mesh"], "scale": object.attrib["scale"] }) Debug.printi("New Object Created from file Name:" + object.attrib["name"], Debug.Level.INFO) self._floor_tex = ET.SubElement(self._root, "floorTexture") if self._floor_tex is None else self._floor_tex self._wall_height = ET.SubElement(self._root, "wallHeight") if self._wall_height is None else self._wall_height self._edge_width = ET.SubElement(self._root, "edgeWidth") if self._edge_width is None else self._edge_width self._sky_texture = ET.SubElement(self._root, "skySphereTexture") if self._sky_texture is None else self._sky_texture self._start_node = ET.SubElement(self._root, "startNode") if self._start_node is None else self._start_node for floor_tex in inputter._root.iter("floorTexture"): self._floor_tex.attrib["val"] = floor_tex.attrib["val"] for wall_height in inputter._root.iter("wallHeight"): self._wall_height.attrib["val"] = wall_height.attrib["val"] for edge_width in inputter._root.iter("edgeWidth"): self._edge_width.attrib["val"] = edge_width.attrib["val"] for sky_tex in inputter._root.iter("skySphereTexture"): self._sky_texture.attrib["val"] = sky_tex.attrib["val"] for start_node in inputter._root.iter("startNode"): self._start_node.attrib["id"] = start_node.attrib["id"] datastore.inform("Environment Edit", data={ "floor_texture": self._floor_tex.attrib["val"], "wall_height": self._wall_height.attrib["val"], "edge_width": self._edge_width.attrib["val"], "sky_texture": self._sky_texture.attrib["val"], "start_node": self._start_node.attrib["id"] }) datastore.inform("VR Edit", data={\ "frame_angle" : inputter._root.attrib["frameAngle"], "distortion" : inputter._root.attrib["distortion"], "windowed" : inputter._root.attrib["windowed"], "eye_height" : inputter._root.attrib["eye"], "minimum_dist_to_wall" : inputter._root.attrib["minDistToWall"] }) self._root.attrib["takeOffAfter"] = "20" self._root.attrib["displays"] = "3,4,1,2,5,6"