class LayerManager(ttk.LabelFrame): def __init__(self, canvas_window, project, *args, **kwargs): ttk.LabelFrame.__init__(self, *args, **kwargs) self.project = project self.canvas_window = canvas_window self.thumbnails = [] self.tiles = [] self.canvas_height = 700 self.trash_image = load_tk_image_from_bytes_array(trash_bytes) self.copy_image = load_tk_image_from_bytes_array(copy_bytes_wide) self.up_image = load_tk_image_from_bytes_array(up_arrow_bytes) self.down_image = load_tk_image_from_bytes_array(down_arrow_bytes) self.name_image = load_tk_image_from_bytes_array(name_symbol_bytes) self.merge_image = load_tk_image_from_bytes_array( merge_down_symbol_bytes) self.new_image = load_tk_image_from_bytes_array(plus_symbol_bytes) self.new_from_image_image = load_tk_image_from_bytes_array( new_layer_from_image_bytes) self.up_carret_image = load_tk_image_from_bytes_array(up_carret_bytes) self.down_carret_image = load_tk_image_from_bytes_array( down_carret_bytes) self.nextid = 0 self.configure(text="Frames") frame_tools_frame = Frame(self) frame_tools_frame.pack(side="top", fill="x", expand="False") self.new_frame_symbol = load_tk_image_from_bytes_array( plus_symbol_bytes) l = Label(frame_tools_frame, image=self.new_frame_symbol) l.pack(side="left") l.bind("<Button-1>", self.new_frame) self.canvas = Canvas(self, relief="sunken") self.canvas.config(width=200, height=700, highlightthickness=0) self.scrollbar = Scrollbar(self) self.scrollbar.config(command=self.on_scroll_bar) self.canvas.config(yscrollcommand=self.scrollbar.set) self.scrollbar.pack(side="right", fill="y") self.canvas.pack(side="right", expand=True, fill="both") self.canvas_frame = Frame(self.canvas, border=0, highlightthickness=0) self.canvas_frame.bind("<MouseWheel>", self._on_mouse_wheel) self.canvas.create_window(0, 0, window=self.canvas_frame, anchor='nw') self.canvas.bind("<MouseWheel>", self._on_mouse_wheel) self.canvas_frame.config(width=200, height=self.winfo_height()) self.canvas.config(scrollregion=(0, 0, 200, self.canvas_height)) self.canvas.bind("<Enter>", self.mouse_enter) self.canvas.bind("<Leave>", self.mouse_leave) self.canvas.bind("<Motion>", self.on_mouse_move) self.canvas.bind("<Button-1>", self.on_click) self.canvas.after_idle(self.canvas_window.refresh) self.bind("<MouseWheel>", self._on_mouse_wheel) def new_frame(self, event=None): self.project.new_frame() self.canvas_window.refresh() def rename_frame(self, frame): name = simpledialog.askstring( "Rename Frame", f"What would you like to rename Frame: {frame.id} to?") if name: frame.set_id(name) self.canvas_window.refresh() def ask_delete_frame(self): if len(self.project.frames) == 1: messagebox.showwarning("Warning", "Cannot delete last frame.") return return messagebox.askyesno( "Delete", "Are you sure you wish to delete this frame?\nThis cannot be undone." ) def delete_frame(self, frame): if self.ask_delete_frame(): self.project.del_frame(frame) self.project.selected_frame = self.project.frames[0] self.canvas.after_idle(self.canvas_window.refresh) def copy_frame(self, frame): self.project.copy_frame(frame) self.canvas.after_idle(self.canvas_window.refresh) def promote_frame(self, frame): self.project.promote_frame(frame) self.canvas.after_idle(self.canvas_window.refresh) def demote_frame(self, frame): self.project.demote_frame(frame) self.canvas.after_idle(self.canvas_window.refresh) def toggle_collapsed(self, frame): self.project.toggle_collapsed(frame) self.canvas.after_idle(self.canvas_window.refresh) def rename_layer(self, frame, layer): name = simpledialog.askstring( "Rename Layer", f"What would you like to rename Layer: {layer.id} to?") if name: layer.set_id(name) self.canvas.after_idle(self.canvas_window.refresh) def new_layer_from_image(self, frame): path = filedialog.askopenfilename() if path: image = Image.open(path) layer = frame.new_layer_from_image(image) self.canvas.after_idle(self.canvas_window.refresh) def new_layer(self, frame): layer = frame.new_layer() frame.selected_layer = layer self.canvas.after_idle(self.canvas_window.refresh) def ask_delete_layer(self, frame, layer): if len(frame.layers) == 1: messagebox.showwarning("Warning", "Cannot delete last layer.") return return messagebox.askyesno( "Delete Layer?", f"Are you sure you wish to delete this layer?\n{layer.id}") def delete_layer(self, frame, layer): if self.ask_delete_layer(frame, layer): frame.del_layer(layer) frame.selected_layer = frame.layers[0] self.canvas.after_idle(self.canvas_window.refresh) def copy_layer(self, frame, layer): frame.copy_layer(layer) self.canvas.after_idle(self.canvas_window.refresh) def promote_layer(self, frame, layer): frame.promote_layer(layer) self.canvas.after_idle(self.canvas_window.refresh) def demote_layer(self, frame, layer): frame.demote_layer(layer) self.canvas.after_idle(self.canvas_window.refresh) def merge_layer_down(self, frame, layer): frame.merge_layer_down(layer) self.canvas.after_idle(self.canvas_window.refresh) def mouse_enter(self, event): pass def mouse_leave(self, event): pass def on_click(self, event): y = int(event.y + (float(self.canvas.yview()[0]) * self.canvas_height)) x = event.x for t in self.tiles: if t.is_in_row(y): mode = t.on_click(x, y) if not mode: t.activate() if type(t) is FrameTile: self.project.selected_frame = t.frame elif type(t) is LayerTile: self.project.selected_frame = t.frame t.frame.selected_layer = t.layer self.canvas_window.refresh() else: t.deactivate() def on_mouse_move(self, event): y = int(event.y + (float(self.canvas.yview()[0]) * self.canvas_height)) x = event.x for t in self.tiles: if t.active: if t.is_in_row(y): continue else: t.deactivate() elif t.is_in_row(y): t.activate() def refresh(self, event=None): self.winfo_toplevel().update_idletasks() self.canvas.delete("all") self.thumbnails = [] self.tiles = [] if self.project.frames: i = 0 y_offset = 10 y_padding = 10 tile_height = 80 x = 10 width = 80 height = tile_height child_offset = 0.5 * tile_height for f in self.project.frames: y = i * tile_height + y_offset + i * y_padding t = FrameTile(self, f) self.tiles.append(t) t.set_dimensions(x, y, width, height) self.place_tile(t) i += 1 firstlayer = True layers = [] if f.layers: if f.collapsed: continue for l in f.layers: floffset = x + 0.5 * child_offset flyoffset = y + tile_height if firstlayer: firstlayer = False self.canvas.create_line( floffset, flyoffset, floffset, flyoffset + y_padding + 0.5 * tile_height) else: self.canvas.create_line( floffset, y + 0.5 * child_offset, floffset, flyoffset + y_padding + 0.5 * tile_height) self.canvas.create_line( floffset, flyoffset + y_padding + 0.5 * tile_height, x + child_offset, flyoffset + y_padding + 0.5 * tile_height, ) y = i * tile_height + y_offset + i * y_padding L = LayerTile(self, l, f) L.set_dimensions(x + child_offset, y, width, height) self.place_tile(L) self.tiles.append(L) i += 1 height = i * (tile_height + y_padding) + y_offset frameheight = self.canvas_frame.winfo_height() height = height if height > frameheight else frameheight self.canvas_height = height self.canvas_frame.config(width=200, height=self.winfo_height()) self.canvas.config(scrollregion=(0, 0, 200, self.canvas_height)) else: print("No frames") def place_tile(self, tile): tn = ImageTk.PhotoImage(tile.get_thumbnail(tile.height - 8)) self.thumbnails.append(tn) tile.references.append( self.canvas.create_image(tile.x + 0.5 * tile.width, tile.y + 0.5 * tile.height, image=tn)) if type(tile) is FrameTile: if tile.frame is self.project.selected_frame: tile.references.append( self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width=3)) else: tile.references.append( self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width=1)) elif type(tile) is LayerTile: if tile.layer is self.project.selected_frame.selected_layer: tile.references.append( self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width=3)) else: tile.references.append( self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width=1)) if tile.active: self.activate_tile() tile.references.append( self.canvas.create_text(tile.x + tile.width + 10, tile.y, font="CourierNew 8", text=tile.id, anchor="nw")) def activate_tile(self, tile): tile.active_references.extend([ self.canvas.create_image(tile.trash_x, tile.trash_y, anchor="nw", image=self.trash_image), self.canvas.create_image(tile.copy_x, tile.copy_y, anchor="nw", image=self.copy_image), self.canvas.create_image(tile.up_x, tile.up_y, anchor="nw", image=self.up_image), self.canvas.create_image(tile.down_x, tile.down_y, anchor="nw", image=self.down_image), self.canvas.create_image(tile.name_x, tile.name_y, anchor="nw", image=self.name_image), ]) if type(tile) is FrameTile: if tile.frame.collapsed: carret_image = self.down_carret_image else: carret_image = self.up_carret_image tile.active_references.extend([ self.canvas.create_image(tile.new_x, tile.new_y, anchor="nw", image=self.new_image), self.canvas.create_image(tile.new_from_image_x, tile.new_from_image_y, anchor="nw", image=self.new_from_image_image), self.canvas.create_image(tile.carret_x, tile.carret_y, anchor="nw", image=carret_image), ]) elif type(tile) is LayerTile: tile.active_references.append( self.canvas.create_image(tile.merge_x, tile.merge_y, anchor="nw", image=self.merge_image)) def deactivate_tile(self, tile): self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width=1) for r in tile.active_references: self.canvas.delete(r) def _on_mouse_wheel(self, event): if platform.system() == 'Windows': self.canvas.yview_scroll(-1 * int(event.delta / 120), 'units') elif platform.system() == 'Darwin': self.canvas.yview_scroll(-1 * int(event.delta), 'units') else: if event.num == 4: self.canvas.yview_scroll(-1, 'units') elif event.num == 5: self.canvas.yview_scroll(1, 'units') def on_scroll_bar(self, move_type, move_units, __=None): if move_type == "moveto": self.canvas.yview("moveto", move_units)
class ClipBoardBox(ttk.Labelframe): def __init__( self, controller, *args, ): ttk.Labelframe.__init__(self, *args, text="Clipboard") self.clipboard = controller.clipboard self.thumbnails = [] self.tiles = [] self.canvas_height = 300 self.trash_image = load_tk_image_from_bytes_array(trash_bytes) self.nextid = 0 self.canvas = Canvas(self, relief="sunken") self.canvas.config(width=200, height=self.canvas_height, highlightthickness=0) self.scrollbar = Scrollbar(self) self.scrollbar.config(command=self.on_scroll_bar) self.canvas.config(yscrollcommand=self.scrollbar.set) self.scrollbar.pack(side="right", fill="y") self.canvas.pack(side="right", expand=True, fill="both") self.canvas_frame = Frame(self.canvas, border=0, highlightthickness=0) self.canvas_frame.bind("<MouseWheel>", self._on_mouse_wheel) self.canvas.create_window(0, 0, window=self.canvas_frame, anchor='nw') self.canvas.bind("<MouseWheel>", self._on_mouse_wheel) self.canvas_frame.config(width=200, height=self.winfo_height()) self.canvas.config(scrollregion=(0, 0, 200, self.canvas_height)) self.canvas.bind("<Enter>", self.mouse_enter) self.canvas.bind("<Leave>", self.mouse_leave) self.canvas.bind("<Motion>", self.on_mouse_move) self.canvas.bind("<Button-1>", self.on_click) self.canvas.after_idle(self.refresh) self.bind("<MouseWheel>", self._on_mouse_wheel) self.grip = ttk.Sizegrip(self) self.grip.place(relx=1.0, rely=1.0, anchor="se") self.grip.bind("<ButtonPress-1>", self.on_press) self.grip.bind("<B1-Motion>", self.on_resize) self.grip.bind("<ButtonRelease-1>", self.on_release) # def on_resize(self, event): # x_change = event.x_root - self.winfo_rootx() # x_change = 1 if x_change < 1 else x_change # y_change = event.y_root - self.winfo_rooty() # y_change = 1 if y_change < 1 else y_change # # self.minsize(self.winfo_reqwidth(), self.winfo_reqheight()) # self.geometry("%sx%s" % (x_change, y_change)) def on_press(self, event): self.grip["cursor"] = "bottom_right_corner" def on_resize(self, event): x_change = event.x_root - self.winfo_rootx() x_change = 1 if x_change < 1 else x_change y_change = event.y_root - self.winfo_rooty() y_change = 1 if y_change < 1 else y_change # self.minsize(self.winfo_reqwidth(), self.winfo_reqheight()) self.geometry("%sx%s" % (x_change, y_change)) def on_release(self, event): self.grip["cursor"] = "arrow" self.refresh() def mouse_enter(self, event): pass def mouse_leave(self, event): pass def on_click(self, event): y = int(event.y + (float(self.canvas.yview()[0]) * self.canvas_height)) x = event.x for t in self.tiles: if t.is_in_row(y): mode = t.on_click(x, y) if not mode: self.clipboard.selected_layer = t.layer t.layer.activate() self.refresh() else: t.deactivate() def on_mouse_move(self, event): y = int(event.y + (float(self.canvas.yview()[0]) * self.canvas_height)) x = event.x for t in self.tiles: if t.active: if t.is_in_row(y): continue else: t.deactivate() elif t.is_in_row(y): t.activate() def refresh(self, event=None): self.winfo_toplevel().update_idletasks() self.canvas.delete("all") self.thumbnails = [] self.tiles = [] if self.clipboard.layers: i = 0 y_offset = 10 y_padding = 10 tile_height = 80 x = 10 width = 80 height = tile_height child_offset = 0.5 * tile_height for c in self.clipboard.layers: y = i * tile_height + y_offset + i * y_padding t = ClipTile(self, c) self.tiles.append(t) t.set_dimensions(x, y, width, height) self.place_tile(t) if c is self.clipboard.selected_layer: self.select_tile(t) i += 1 height = i * tile_height + y_offset + i * y_padding frameheight = self.canvas_frame.winfo_height() height = height if height > frameheight else frameheight self.canvas_height = height self.canvas_frame.config(width=200, height=self.canvas_height) self.canvas.config(scrollregion=(0, 0, 200, self.canvas_height)) else: pass def place_tile(self, tile): tn = ImageTk.PhotoImage(tile.get_thumbnail(tile.height)) self.thumbnails.append(tn) tile.references.append( self.canvas.create_image(tile.x + 0.5 * tile.width, tile.y + 0.5 * tile.height, image=tn)) tile.references.append( self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width=1)) tile.references.append( self.canvas.create_text(tile.x + tile.width + 10, tile.y, font="CourierNew 8", text=tile.id, anchor="nw")) tile.references.append( self.canvas.create_text( tile.x + tile.width + 10, tile.y + 10, font="CourierNew 8", text=f"Size: {tile.layer.width}px x {tile.layer.height}px", anchor="nw")) def activate_tile(self, tile): tile.active_references.extend([ self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width=2), self.canvas.create_image(tile.trash_x, tile.trash_y, anchor="nw", image=self.trash_image), ]) def deactivate_tile(self, tile): self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width=1) for r in tile.active_references: self.canvas.delete(r) def select_tile(self, tile): tile.references.append( self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width=3)) def _on_mouse_wheel(self, event): if platform.system() == 'Windows': self.canvas.yview_scroll(-1 * int(event.delta / 120), 'units') elif platform.system() == 'Darwin': self.canvas.yview_scroll(-1 * int(event.delta), 'units') else: if event.num == 4: self.canvas.yview_scroll(-1, 'units') elif event.num == 5: self.canvas.yview_scroll(1, 'units') def on_scroll_bar(self, move_type, move_units, __=None): if move_type == "moveto": self.canvas.yview("moveto", move_units) def delete_layer(self, layer): self.clipboard.del_layer(layer) self.clipboard.selected_layer = self.clipboard.layers[ 0] if self.clipboard.layers else None self.canvas.after_idle(self.refresh)