class TShapesWindow(Frame): """ Class represents auxiliary window containg shapes information. :param master: master window object. :type master: tkinter.Tk :param app: main app object. :type app: TApp """ def __init__(self, master, TApp): """ Call the parent class constructor and initialise object variables. """ super().__init__() self.TApp = TApp self.round_digits = TTicksSettings.ROUND_DIGITS self.init_widgets() def init_widgets(self): """ Init frame widgets. """ # Listbox self.shapes_list = Listbox(self, exportselection=False, selectmode=EXTENDED) self.grid_columnconfigure(0, weight=1, minsize=300) self.grid_rowconfigure(0, weight=1) self.shapes_list.grid(row=0, column=0, sticky=NSEW, padx=(5, 25), pady=5) self.shapes_list.bind("<<ListboxSelect>>", self.shapes_list_selected_item) # Listbox's yscrollbar self.shapes_list_scrollbar = Scrollbar(self, orient = VERTICAL, \ command = self.shapes_list.yview) self.shapes_list_scrollbar.grid(row=0, column=0, sticky=NS + E, padx=(0, 5), pady=5) self.shapes_list.config(yscrollcommand=self.shapes_list_scrollbar.set) # Drop down menu with materials self.material_box = Combobox(self, values=["pec", "free_space"]) self.material_box.grid(row=1, column=0, sticky=EW, padx=5, pady=5) self.material_box.bind("<<ComboboxSelected>>", self.assign_material_to_shape) # Right click popup menu self.init_popup_menu() self.shapes_list.bind("<Button-3>", self.show_popoup_menu) # Delete key removes selected shape(s) self.shapes_list.bind("<Delete>", self.remove_shape) def update_list(self, shapes, *, swap=False): """ Update shapes list. :param swap: shapes list selection swap toggle. :type swap: boolean """ selection = self.shapes_list.curselection() try: shape_num = selection[0] except: shape_num = 0 self.shapes_list.delete(0, END) coord_string = "" for i, single_shape in enumerate(shapes): if (single_shape.type == "Rectangle"): coord_string = "(" + str(round(single_shape.point1_mod.x, self.round_digits)) + \ ", " + str(round(single_shape.point1_mod.y, self.round_digits)) + \ "), (" + str(round (single_shape.point2_mod.x, self.round_digits)) + \ ", " + str(round(single_shape.point2_mod.y, self.round_digits)) + ")" elif (single_shape.type == "Cylinder"): coord_string = "(" + str(round(single_shape.centre_mod.x, self.round_digits)) + \ ", " + str(round(single_shape.centre_mod.y, self.round_digits)) + \ "), " + str(round(single_shape.radius_mod, self.round_digits)) elif (single_shape.type == "CylinSector"): coord_string = "(" + str(round(single_shape.centre_mod.x, self.round_digits)) + \ ", " + str(round(single_shape.centre_mod.y, self.round_digits)) + \ "), " + str(round(single_shape.radius_mod, self.round_digits)) + \ ", " + str(round(single_shape.start, self.round_digits)) + \ ", " + str(round(single_shape.extent, self.round_digits)) elif (single_shape.type == "Polygon"): coord_string = str(len(single_shape.points)) self.shapes_list.insert( i, str(i + 1) + ". " + single_shape.type + ": " + coord_string) coord_string = "" if (not swap): self.shapes_list.select_clear(0, END) if (shape_num >= self.shapes_list.size()): self.shapes_list.selection_set(shape_num - 1) self.shapes_list.activate(shape_num) else: for item in selection: self.shapes_list.selection_set(item) self.shapes_list.activate(shape_num) def assign_material_to_shape(self, event): """ Assign material to a shape. :param event: event evoking this method (listbox select). :type event: tkinter.Event """ material = self.material_box.get() try: shape_num = (self.shapes_list.curselection())[0] except: return else: if (shape_num < 0 or material == ""): return else: try: self.TApp.shapes[shape_num].material = material except Exception as message: messagebox.showerror("Material assignment error!", message) return def shapes_list_selected_item(self, event): """ Handle listbox selection event. :param event: event evoking this method (listbox select). :type event: tkinter.Event """ # Add multiple selection self.shapes_list.focus_force() try: shape_num = (self.shapes_list.curselection())[0] selection = self.shapes_list.curselection() except IndexError: return except Exception as message: messagebox.showerror("Error while picking shape!", message) if (shape_num < 0): return else: try: shape = self.TApp.shapes[shape_num] except Exception as message: messagebox.showerror("Materials list error", message) return self.material_box.set(str(shape.material)) for single_shape in self.TApp.shapes: single_shape.width = 1 for item in selection: self.TApp.shapes[item].width = 2 self.TApp.main_canvas.delete("all") for item in selection: self.shapes_list.selection_set(item) self.TApp.canvas_refresh() def init_popup_menu(self): """ Init shapes pane pup-up menu. """ self.popup_menu = Menu(self, tearoff=0) self.popup_menu.add_command(label="Edit shape", command=self.edit_shape) self.popup_menu.add_command(label="Change shape colour", command=self.change_shape_colour) self.popup_menu.add_command(label="Remove shape(s)", command=self.remove_shape) self.popup_menu.add_separator() self.popup_menu.add_command(label="Add vertex to polygon", command=self.add_vertex_to_polygon) self.popup_menu.add_separator() self.popup_menu.add_command(label="Copy shape", command=self.copy_shape) self.popup_menu.add_command(label="Paste shape", command=self.paste_shape) self.popup_menu.add_separator() self.popup_menu.add_command(label="Move up", command=self.move_shape_up) self.popup_menu.add_command(label="Move down", command=self.move_shape_down) self.popup_menu.add_command(label="Move to top", command=self.move_shape_top) self.popup_menu.add_command(label="Move to bottom", command=self.move_shape_bottom) def show_popoup_menu(self, event): """ Show shapes list pop-up menu. :param event: event evoking this method (RMB click). :type event: tkinter.Event """ try: self.popup_menu.post(event.x_root, event.y_root) finally: self.popup_menu.grab_release() def move_shape_up(self): """ Move a shape one place up the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: try: self.TApp.shapes.insert(shape_num - 1, self.TApp.shapes.pop(shape_num)) self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh(swap=True) self.shapes_list.selection_set(shape_num - 1) self.shapes_list.activate(shape_num - 1) except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def move_shape_down(self): """ Move a shape one place down the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: try: self.TApp.shapes.insert(shape_num + 1, self.TApp.shapes.pop(shape_num)) self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh(swap=True) self.shapes_list.selection_set(shape_num + 1) self.shapes_list.activate(shape_num + 1) except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def move_shape_top(self): """ Move a shape to the top of the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: try: self.TApp.shapes.insert(0, self.TApp.shapes.pop(shape_num)) self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh(swap=True) self.shapes_list.selection_set(0) self.shapes_list.activate(0) # self.shapes_list.focus_set () except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def move_shape_bottom(self): """ Move a shape to the bottom of the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: try: self.TApp.shapes.append(self.TApp.shapes.pop(shape_num)) self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh(swap=True) self.shapes_list.selection_set(END) self.shapes_list.activate(END) except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def edit_shape(self): """ Edit selected shape on the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: self.TApp.operations.append(TOperation("edit", shape = \ deepcopy(self.TApp.shapes[shape_num]), \ num = shape_num)) if (self.TApp.shapes[shape_num].type == "Rectangle"): self.TApp.edit_rectangle(shape_num) elif (self.TApp.shapes[shape_num].type == "Cylinder"): self.TApp.edit_cylin(shape_num) elif (self.TApp.shapes[shape_num].type == "CylinSector"): self.TApp.edit_cylin_sector(shape_num) elif (self.TApp.shapes[shape_num].type == "Polygon"): self.TApp.edit_polygon(shape_num) def change_shape_colour(self): """ Change selected shape on the shapes list colour. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: self.TApp.change_shape_colour(shape_num=shape_num) def remove_shape(self, event=None): """ Remove selected shape on the shapes list. """ try: selection = self.shapes_list.curselection() except IndexError: return if (len(selection) == 0): return else: try: for item in reversed(selection): del self.TApp.shapes[item] self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh() except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def add_vertex_to_polygon(self): """ Add a vertex to selected polygon on the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return input_str = simpledialog.askstring("Input coordinates", "Give mew vertex's coordinates") point_mod_x, point_mod_y = [float(val) for val in input_str.split()] if (self.TApp.shapes[shape_num].type == "Polygon" and shape_num > -1): self.TApp.shapes[shape_num].add_vertex(x_mod=point_mod_x, y_mod=point_mod_y) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh() def copy_shape(self): """ Copy selected shape on the shapes list. """ try: shape_num = self.shapes_list.curselection()[0] except: shape_num = -1 if (shape_num > -1): try: self.TApp.copy_shape(shape_num=shape_num) except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) def paste_shape(self, *, deltax=15, deltay=15): """ Paste selected shape from buffer. :param deltax: pasted shape offset in x direction in pixels. :type deltax: integer :param deltay: pasted shape offset in y direction in pixels. :type deltay: integer """ self.TApp.paste_ctrl_v(Event())
class ColormapChooser(Toplevel): def __init__(self,master,cmap=None): Toplevel.__init__(self,master) # update title self.title("Choose a colormap") # create frame frm = Frame(self) # create listbox self.clist = Listbox(frm) # bind double click self.clist.bind('<Button-1>',self.cmapSelect) # get all colormaps for ci,(kk,_) in enumerate(filter(lambda x : isinstance(x[1],Colormap),vars(matplotlib.cm).items())): self.clist.insert(ci,kk) # scrollbar scroll = Scrollbar(frm,orient='vertical') scroll.config(command=self.clist.yview) # currently selected colormap # if nothing is given, get matplotlib default if cmap == None: self.curr_cmap = getattr(matplotlib.cm,matplotlib.rcParams['image.cmap']) else: # if user specifies a string if type(cmap) == str: # attempt to get index of colormap from list # if it fails, reverts to default try: idx = self.self.clist.get(0,END).index(cmap) self.self.clist.select(idx) self.curr_cmap = getattr(matplotlib.cm,self.self.clist.get(0,END)[idx]) except ValueError: self.curr_cmap = getattr(matplotlib.cm,matplotlib.rcParams['image.cmap']) # if the user passs a Colormap directly, store that elif isinstance(cmap,Colormap): self.curr_cmap = cmap # if it's something else # print error message and set current colormap to None else: print(f"Unsupported colormap value {cmap}!",file=sys.stderr) self.curr_cmap = None # add buttons btt_frm = Frame(self) select_btt = Button(btt_frm,text="Select",command=self.enter_handler) cancel_btt = Button(btt_frm,text="Cancel",command=self.cancel_handler) # keyboard handlers for if the button is currently selected select_btt.bind('<KeyPress-Return>', func=self.enter_handler) cancel_btt.bind('<KeyPress-Return>', func=self.cancel_handler) ## pack self.clist.grid(row=0,column=0,sticky='nswe') scroll.grid(row=0,column=1,sticky='ns') frm.grid(row=0,column=0,sticky='nswe') select_btt.grid(row=0,column=0,sticky='ew') cancel_btt.grid(row=0,column=1,sticky='ew') btt_frm.grid(row=1,column=0,columnspan=2,sticky='ew') # colormap listbox double click handler # gets colormap matching key and updates selected colormap def cmapSelect(self,event): selection = event.widget.curselection() if len(selection)==0: return print(event.widget.get(selection[0])) self.curr_cmap = getattr(matplotlib.cm,event.widget.get(selection[0])) def enter_handler(self): selection = self.clist.curselection() self.curr_cmap = self.clist.get(selection[0]) self.destroy() def cancel_handler(self): self.curr_cmap = None self.destroy() def show(self): self.wm_deiconify() self.clist.focus_force() self.wait_window() return self.curr_cmap