class OptionWindow(Toplevel): def __call__(self, options=[], display=True): self.options = options self.listbox.delete(0, END) for key, value in options: self.listbox.insert(END, key) if display: self.display() def __init__(self): Toplevel.__init__(self, master=root) self.options = None self.title('Matches') self.listbox = Listbox(master=self) self.listbox.pack(expand=True, fill=BOTH, side=TOP) self.listbox.focus_set() self.listbox.activate(0) self.listbox.selection_set(0) self.listbox.config(width=50) self.listbox.bind('<Key-h>', lambda event: self.listbox.event_generate('<Left>')) self.listbox.bind('<Key-l>', lambda event: self.listbox.event_generate('<Right>')) self.listbox.bind('<Key-k>', lambda event: self.listbox.event_generate('<Up>')) self.listbox.bind('<Key-j>', lambda event: self.listbox.event_generate('<Down>')) self.listbox.bind('<Escape>', lambda event: self.close()) self.protocol("WM_DELETE_WINDOW", self.close) self.transient(root) self.withdraw() def display(self): self.grab_set() self.deiconify() self.listbox.focus_set() # self.wait_window(self) def close(self): # When calling destroy or withdraw without # self.deoiconify it doesnt give back # the focus to the parent window. self.deiconify() self.grab_release() self.withdraw()
def openStats(handler): statsApi = get( "https://raw.githubusercontent.com/revoxhere/duco-statistics/master/api.json", data=None) if statsApi.status_code == 200: #Check for reponse statsApi = (statsApi.json()) print(statsApi) statsWindow = Toplevel() statsWindow.resizable(False, False) statsWindow.title("Duino-Coin Wallet - Stats") statsWindow.configure(background=backgroundColor) statsWindow.transient([root]) textFont3 = Font(statsWindow, size=14, weight="bold") textFont = Font(statsWindow, size=12, weight="normal") Label(statsWindow, text="Duco Statistics - WIP", background=backgroundColor, foreground=foregroundColor, font=textFont3).grid(row=0, column=0) Active_workers_listbox = Listbox(statsWindow, exportselection=False, background=backgroundColor, foreground=foregroundColor, selectbackground="#7bed9f", border="0", font=textFont, width="20", height="13") Active_workers_listbox.grid(row=1, column=0, sticky=W) i = 0 for worker in statsApi['Active workers']: Active_workers_listbox.insert(i, worker) i += 1 Active_workers_listbox.select_set(32) Active_workers_listbox.event_generate("<<ListboxSelect>>") Top_10_listbox = Listbox(statsWindow, exportselection=False, background=backgroundColor, foreground=foregroundColor, selectbackground="#7bed9f", border="0", font=textFont, width="33", height="13") Top_10_listbox.grid(row=1, column=1, sticky=W) i = 0 for rich in statsApi['Top 10 richest miners']: Top_10_listbox.insert(i, statsApi['Top 10 richest miners'][i]) i += 1 Top_10_listbox.select_set(32) Top_10_listbox.event_generate("<<ListboxSelect>>") statsWindow.mainloop()
class SelectMultiple(TwoCells): def __init__(self, parent, text, values, **kwargs): super().__init__(parent, **kwargs) self.label = Label(self.frame, text=text) self.label.grid(column=0, row=0, padx=5, pady=5, sticky=W) self.selected_options = StringVar(self.frame) self.selected_options.set(values) self.listbox = Listbox(self.frame, listvariable=self.selected_options, selectmode=MULTIPLE) self.listbox.grid(column=1, row=0, padx=5, pady=5, sticky=E) def disable(self): self.listbox.configure(state=DISABLED) return self def enable(self): self.listbox.configure(state=NORMAL) return self def set(self, values): if values: self.enable() else: self.disable() self.listbox.selection_clear(0, self.listbox.size()) for value in values: for index in range(self.listbox.size()): if value == self.listbox.get(index): self.listbox.selection_set(index) self.listbox.event_generate("<<ListboxSelect>>") def get(self): return [self.listbox.get(i) for i in self.listbox.curselection()]
class model_select(Frame): def __init__(self,controller,model_list,current_model,master,*args,**kwargs): self.controller = controller self.model_list = model_list self.current_model = current_model # Dibujar widget super().__init__(master, *args, **kwargs) self.listbox = Listbox(self, exportselection=False) self.listbox.bind('<<ListboxSelect>>', self.selection) self.listbox.pack() for item in self.model_list: self.listbox.insert(END, item) self.listbox.select_set(0) self.listbox.event_generate("<<ListboxSelect>>") def selection(self,x): w = x.widget index = int(w.curselection()[0]) value = w.get(index) print(value) # Send message to interface to load another model f_path = os.path.join(config_folder, value) self.current_model.change_model(f_path) self.controller.event_change_model(self.current_model) pass
class mask_select(Frame): def __init__(self, controller, current_model : model_manager_obj, master, *args, **kwargs): self.controller = controller super().__init__(master, *args, **kwargs) self.listbox = Listbox(self, exportselection=False) self.listbox.pack(fill="both", expand=True) self.listbox.bind("<<ListboxSelect>>",self.selection) Label(self,text="New mask file: ").pack() self.entry_text = StringVar() Entry(self,textvariable=self.entry_text).pack() Button(self,text='Create',command=self.add_new).pack() self.update_model(current_model) def update_model(self, current_model): self.model = current_model self.mask_file_list = self.model.get_mask_file_list() self.current_mask_file=self.model.get_current_mask_file() self.listbox.delete(0, END) for item in self.mask_file_list: self.listbox.insert(END, item) self.listbox.select_set(0) self.listbox.event_generate("<<ListboxSelect>>") def selection(self,event): w = event.widget index = int(w.curselection()[0]) value = w.get(index) selected = value self.controller.event_change_mask_file(selected) pass def add_new(self): new = self.entry_text.get() self.controller.event_add_mask_file(new) pass
class Controller: SNAP_RADIUS = 5 def __init__(self, tk: Tk): tk.title("Layered Polygons") menubar = Menu(tk) menu_file = Menu(menubar, tearoff=0) menu_file.add_command(label="New...", command=self._new_scene) menu_file.add_command(label="Open...", command=self._open_scene) menu_file.add_separator() menu_file.add_command(label="Save", command=self._save_scene) menu_file.add_command(label="Save As...", command=self._save_scene_as) menu_file.add_separator() menu_export = Menu(menu_file, tearoff=0) menu_export.add_command(label="Wavefront (.obj)...", command=self._export_obj) menu_file.add_cascade(label="Export As", menu=menu_export) menu_file.add_separator() menu_file.add_command(label="Quit", command=self._quit_app) menubar.add_cascade(label="File", menu=menu_file) tk.config(menu=menubar) paned = PanedWindow(tk, relief=RAISED) paned.pack(fill=BOTH, expand=1) frame = Frame(paned) paned.add(frame) self._canvas = LayPolyCanvas(frame) bar_x = Scrollbar(frame, orient=HORIZONTAL) bar_x.pack(side=BOTTOM, fill=X) bar_x.config(command=self._canvas.xview) bar_y = Scrollbar(frame, orient=VERTICAL) bar_y.pack(side=RIGHT, fill=Y) bar_y.config(command=self._canvas.yview) self._canvas.config(xscrollcommand=bar_x.set, yscrollcommand=bar_y.set) self._canvas.pack(side=LEFT, expand=True, fill=BOTH) # Thanks to the two guys on Stack Overflow for that! # ( http://stackoverflow.com/a/7734187 ) self._layer_list = Listbox(paned, selectmode=SINGLE) paned.add(self._layer_list) self._scene = None self._current_layer = None self._is_drawing_polygon = False self._tk = tk self._canvas.bind("<Button-1>", self._canvas_left_click) self._canvas.bind("<Button-3>", self._canvas_right_click) self._canvas.bind("<Motion>", self._canvas_mouse_moved) self._layer_list.bind("<<ListboxSelect>>", self._layer_change) self._current_path = None def _canvas_left_click(self, event): if not self._scene or not self._current_layer: return x, y = self._canvas.window_to_canvas_coords(event.x, event.y) if self._is_drawing_polygon: polygon = self._current_layer.get_polygon_at(-1) # Move vtx away from mouse to not interfere with search for closest polygon.get_vertex_at(-1).\ set_coords(x-self.SNAP_RADIUS, y-self.SNAP_RADIUS) closest_vertex = self._current_layer.get_closest_vertex( x, y, self.SNAP_RADIUS) if closest_vertex: polygon.remove_vertex_at(-1) if closest_vertex is polygon.get_vertex_at(0): self._is_drawing_polygon = False else: polygon.add_vertex(closest_vertex) polygon.add_vertex(Vertex(x, y)) else: polygon.get_vertex_at(-1)\ .set_coords(x, y) polygon.add_vertex(Vertex(x, y)) self._canvas.notify_polygon_change(self._current_layer .get_polygon_count()-1) else: # Create start vertex or use already existing one start_vertex = self._current_layer\ .get_closest_vertex(x, y, self.SNAP_RADIUS) if not start_vertex: start_vertex = Vertex(x, y) # Vertex for mouse cursor next_vertex = Vertex(x, y) self._current_layer.add_polygon( Polygon([start_vertex, next_vertex])) self._is_drawing_polygon = True self._canvas.notify_layer_change() def _canvas_right_click(self, event): if not self._current_layer: return if self._is_drawing_polygon: self._current_layer.remove_polygon_at(-1) self._is_drawing_polygon = False else: x, y = self._canvas.window_to_canvas_coords(event.x, event.y) for i in range(0, self._current_layer.get_polygon_count()): if self._current_layer.get_polygon_at(i).contains(x, y): self._current_layer.remove_polygon_at(i) break self._canvas.notify_layer_change() def _canvas_mouse_moved(self, event): if self._is_drawing_polygon: x, y = self._canvas.window_to_canvas_coords(event.x, event.y) self._current_layer.get_polygon_at(-1).get_vertex_at(-1)\ .set_coords(x, y) self._canvas.notify_polygon_change(self._current_layer .get_polygon_count()-1) def _layer_change(self, event): selection = self._layer_list.curselection() if len(selection) > 0 and self._scene: layer = self._scene.get_layer_at(selection[0]) if layer: self._is_drawing_polygon = False self._current_layer = layer self._canvas.notify_new_layer(self._current_layer) def _set_scene(self, scene: Scene) -> bool: if scene.get_layer_count() <= 0: messagebox.showerror("Error!", "Scene has no layers!") return False self._scene = scene # Prepare canvas # TODO Extra 10px padding for canvas width, height = self._scene.get_size() self._canvas.config(scrollregion=(0, 0, width, height)) # Empty listbox, fill it, select first entry self._layer_list.delete(0, self._layer_list.size()-1) for i in range(0, self._scene.get_layer_count()): self._layer_list.insert(i, self._scene.get_layer_at(i).get_name()) self._layer_list.selection_set(0) self._layer_list.event_generate("<<ListboxSelect>>") return True def _set_current_path(self, path): self._current_path = path if path: self._tk.title(path + " - Layered Polygons") else: self._tk.title("Untitled - Layered Polygons") def _new_scene(self): path = filedialog.askopenfilename(defaultextension=".ora", filetypes=[("OpenRaster files", ".ora")]) if not path: return scene = ora.read(path) if not scene: messagebox.showerror("Error!", "File could not be opened!") return if self._set_scene(scene): self._set_current_path(None) def _open_scene(self): path = filedialog.askopenfilename(defaultextension=".lp", filetypes=[("Layered polygons" " files", ".lp")]) if not path: return scene = lp.read(path) if not scene: messagebox.showerror("Error!", "File could not be opened!") return if self._set_scene(scene): self._set_current_path(path) def _save_scene_help(self, path): if lp.save(path, self._scene): self._set_current_path(path) else: messagebox.showerror("Error!", "File could not be saved!") def _save_scene(self): if not self._current_path: self._save_scene_as() return self._save_scene_help(self._current_path) def _save_scene_as(self): if not self._scene: return path = filedialog.asksaveasfilename(defaultextension=".lp", filetypes=[("Layered polygons" " files", ".lp")]) if path: self._save_scene_help(path) def _export_obj(self): if not self._scene: return path_obj = filedialog.asksaveasfilename(defaultextension=".obj", filetypes=[("Wavefront object" " files", ".obj")]) if not path_obj: return path_obj, path_mtl, path_data = obj.get_paths(path_obj) obj.save(path_obj, path_mtl, path_data, self._scene) def _quit_app(self): self._tk.quit() exit()
class Application(tk.Frame): def __init__(self, parent, *args, **kwargs): self.entrys = [] self.textboxes = [] self.asterisks = 1 self.selected_index = 0 self.previous_index = None self.update_flag = False self.action_flag = 0 self.previous_key = '' self.new_key = '' #init frame print('INITIALISING FRAME') tk.Frame.__init__(self, parent, *args, **kwargs) self.parent = parent #Set window name, size parent.title('SwiftPass') parent.geometry('700x700') #on mouse1 click, deselect listbox parent.bind('<ButtonPress-1>', self.deselect_lb_item) self.generate_widgets() self.populate_data() def generate_widgets(self): print('GENERATING WIDGETS') #Service ID self.serviceid_text = StringVar() self.serviceid_label = Label(self.parent, text='Service ID', font=( 'bold', 14, ), pady=20, padx=5) self.serviceid_label.grid(row=0, column=0, sticky=W) self.serviceid_entry = Entry(self.parent, textvariable=self.serviceid_text) self.serviceid_entry.grid(row=0, column=1) self.entrys.append(self.serviceid_entry) self.textboxes.append(self.serviceid_text) #Service Name self.service_text = StringVar() self.service_label = Label(self.parent, text='Service Name', font=('bold', 14), pady=20, padx=5) self.service_label.grid(row=1, column=0, sticky=W) self.service_entry = Entry(self.parent, textvariable=self.service_text) self.service_entry.grid(row=1, column=1) self.entrys.append(self.service_entry) self.textboxes.append(self.service_text) #URL self.url_text = StringVar() self.url_label = Label(self.parent, text='URL', font=('bold', 14), pady=20, padx=5) self.url_label.grid(row=2, column=0, sticky=W) self.url_entry = Entry(self.parent, textvariable=self.url_text) self.url_entry.grid(row=2, column=1) self.entrys.append(self.url_entry) self.textboxes.append(self.url_text) #Username self.username_text = StringVar() self.username_label = Label(self.parent, text='Username', font=('bold,', 14), pady=20, padx=5) self.username_label.grid(row=3, column=0, stick=W) self.username_entry = Entry(self.parent, textvariable=self.username_text) self.username_entry.grid(row=3, column=1) self.entrys.append(self.username_entry) self.textboxes.append(self.username_text) #Password self.password_text = StringVar() self.password_label = Label(self.parent, text='Password', font=('bold', 14), pady=20, padx=5) self.password_label.grid(row=4, column=0, sticky=W) self.password_entry = Entry(self.parent, textvariable=self.password_text) self.password_entry.grid(row=4, column=1) self.entrys.append(self.password_entry) self.textboxes.append(self.password_text) #Listbox self.password_list = Listbox(self.parent, height=20, width=50, border=1) self.password_list.grid(row=5, column=0, padx=5, columnspan=3, rowspan=6) self.password_list.bind('<<ListboxSelect>>', self.select) #self.scrollbar = tk.Scrollbar(self.parent) #self.scrollbar.grid(row=3, column=2) #self.password_list.config(yscrollcommand=self.scrollbar.set) #self.scrollbar.config(command=self.password_list.yview) #Add button self.add_btn = Button(self.parent, text='Add Service', width=12, command=self.add_service, fg='black', foreground='black', highlightbackground='black') self.add_btn.grid(row=0, column=3, pady=20, padx=5) #Remove button self.remove_btn = Button(self.parent, text='Remove Service', width=12, command=self.remove_service, fg='black', foreground='black', highlightbackground='black') self.remove_btn.grid(row=0, column=4, pady=20, padx=5) #Edit button self.edit_btn = Button(self.parent, text='Edit Service', width=12, command=self.edit_service, fg='black', foreground='black', highlightbackground='black') self.edit_btn.grid(row=1, column=3, pady=20, padx=5) #Show Password button self.show_passwords_btn = Button(self.parent, text='Show Passwords', width=12, command=self.toggle_show_passwords, fg='black', foreground='black', highlightbackground='black') self.show_passwords_btn.grid(row=1, column=4, pady=20, padx=5) #Save button self.save_btn = Button(self.parent, text='Save', width=12, command=self.save, state='disabled', fg='black', foreground='black', highlightbackground='black') self.save_btn.grid(row=2, column=3, pady=20, padx=5) #Cancel button self.cancel_btn = Button(self.parent, text='Cancel', width=12, command=self.cancel, state='disabled', fg='black', foreground='black', highlightbackground='black') self.cancel_btn.grid(row=2, column=4, pady=20, padx=5) #Copy URL button self.copy_url_btn = Button( self.parent, text='Copy', width=12, command=lambda: self.clipboard(self.url_entry.get()), fg='black', foreground='black', highlightbackground='black') self.copy_url_btn.grid(row=2, column=2) #Copy Username button self.copy_uname_btn = Button( self.parent, text='Copy', width=12, command=lambda: self.clipboard(self.username_entry.get()), fg='black', foreground='black', highlightbackground='black') self.copy_uname_btn.grid(row=3, column=2) #Copy Password button self.copy_pass_btn = Button( self.parent, text='Copy', width=12, command=lambda: self.clipboard( db.get_password(self.serviceid_entry.get())), fg='black', foreground='black', highlightbackground='black') self.copy_pass_btn.grid(row=4, column=2) #self.toggle_tb(1) self.generate_secure_password_32_btn = Button( self.parent, text='Generate (32)', width=12, fg='black', foreground='black', highlightbackground='black', command=lambda: self.password_text.set(self.generate_password(32)), state='disabled') self.generate_secure_password_32_btn.grid(row=4, column=3) self.generate_secure_password_64_btn = Button( self.parent, text='Generate (64)', width=12, fg='black', foreground='black', highlightbackground='black', command=lambda: self.password_text.set(self.generate_password(64)), state='disabled') self.generate_secure_password_64_btn.grid(row=4, column=4) #Deselect currently selected listbox item def deselect_lb_item(self, event): print('DESELECTED LISTBOX') if self.selected_index == self.previous_index: self.password_list.selection_clear(0, tk.END) self.previous_index = self.password_list.curselection() def populate_data(self): #clear listbox print('POPULATING DATA') self.password_list.delete(0, END) if self.asterisks == 1: for row in db.fetch(): #anonyimise password self.anonyimise_listbox(row) if self.asterisks == 0: for row in db.fetch(): #insert row to listbox self.password_list.insert(END, row) self.password_list.select_set(self.selected_index) self.password_list.event_generate("<<ListboxSelect>>") def anonyimise_listbox(self, data): print('ANONYIMISING DATA') #anonyimise password pwd = self.anonymise(str(data[4])) data_list = list(data) data_list[4] = pwd new_row = tuple(data_list) #insert password self.password_list.insert(END, new_row) def unan_listbox(self): print('UNANYMISING DATA') #reinit data self.populate_data() def toggle_show_passwords(self): print('TOGGLING SHOW PASSWORDS') changed_flag = False if self.asterisks == 1: self.asterisks = 0 #reinit data self.populate_data() changed_flag = True if self.asterisks == 0 and changed_flag is False: self.asterisks = 1 #clear listbox self.password_list.delete(0, END) for row in db.fetch(): #anonymise password self.anonyimise_listbox(row) self.password_list.select_set(self.selected_index) self.password_list.event_generate("<<ListboxSelect>>") def anonymise(self, password): #return anonyimised password return "*" * len(password) def add_service(self): print('BEGIN ADD SERVICE') self.clear() self.toggle_tb(0) self.action_flag = 'add' self.add_btn["state"] = "disabled" self.remove_btn["state"] = "disabled" self.edit_btn["state"] = "disabled" self.show_passwords_btn["state"] = "disabled" self.password_list["state"] = "disabled" self.save_btn["state"] = "active" self.cancel_btn["state"] = "active" self.generate_secure_password_32_btn["state"] = "active" self.generate_secure_password_64_btn["state"] = "active" def cancel(self): print('CANCEL ACTION') self.toggle_tb(1) self.save_btn["state"] = "disabled" self.cancel_btn["state"] = "disabled" self.add_btn["state"] = "active" self.remove_btn["state"] = "active" self.edit_btn["state"] = "active" self.password_list["state"] = "normal" self.show_passwords_btn["state"] = "active" self.generate_secure_password_32_btn["state"] = "disabled" self.generate_secure_password_64_btn["state"] = "disabled" return def save(self): print('SAVING ACTION') allow = False if self.action_flag == 'add': if self.insert_service() != -1: allow = True if self.action_flag == 'edit': if self.commit_edit() != -1: allow = True if allow is True: self.toggle_tb(1) self.save_btn["state"] = "disabled" self.cancel_btn["state"] = "disabled" self.add_btn["state"] = "active" self.remove_btn["state"] = "active" self.edit_btn["state"] = "active" self.show_passwords_btn["state"] = "active" self.password_list["state"] = "normal" self.generate_secure_password_32_btn["state"] = "disabled" self.generate_secure_password_64_btn["state"] = "disabled" self.populate_data() def insert_service(self): print('ATTEMPTING INSERT TO DATABASE') if self.check() == -1: return -1 #insert data to be database print('INSERT SUCCESSFUL') db.insert(self.service_entry.get(), self.url_entry.get(), str(self.username_entry.get()), self.password_entry.get()) self.populate_data() def check(self): #length check if len(self.service_entry.get()) == 0 or len( self.username_entry.get()) == 0 or len( self.url_entry.get()) == 0 or len( self.password_entry.get()) == 0: #show message box mb.showerror("Error", "Please ensure all fields contain data") return -1 return 1 def edit_service(self): self.toggle_tb(0) self.add_btn["state"] = "disabled" self.remove_btn["state"] = "disabled" self.edit_btn["state"] = "disabled" self.show_passwords_btn["state"] = "disabled" self.password_list["state"] = "disabled" self.save_btn["state"] = "active" self.cancel_btn["state"] = "active" self.action_flag = 'edit' self.generate_secure_password_32_btn["state"] = "active" self.generate_secure_password_64_btn["state"] = "active" def commit_edit(self): if self.check() == -1: return #update database db.update(self.serviceid_entry.get(), self.service_entry.get(), self.url_entry.get(), str(self.username_entry.get()), self.password_entry.get()) self.populate_data() def remove_service(self): id = self.serviceid_entry.get() db.remove(id) self.populate_data() def clear(self): #clear textboxes for tb in self.textboxes: tb.set('') return def toggle_tb(self, flag): #enable/disable textboxes if flag == 1: for tb in self.entrys: tb.config(state='disabled') return for tb in self.entrys: tb.config(state='normal') self.serviceid_entry.config(state='disabled') def select(self, event): #insert selected listbox item into textboxes sel = self.password_list.get(self.password_list.curselection()) self.selected_index = self.password_list.curselection() service_id = sel[0] service_name = sel[1] url = sel[2] username = sel[3] password = sel[4] self.serviceid_text.set(service_id) self.service_text.set(service_name) self.url_text.set(url) self.username_text.set(username) #check if we need to anonymise if self.asterisks == 1: password = self.anonymise(password) self.password_text.set(password) def clipboard(self, arg): #copy to clipboard cbl.copy(arg) def generate_password(self, len): return ''.join(random.choice(string.ascii_letters) for i in range(len))
class Controller: SNAP_RADIUS = 5 def __init__(self, tk: Tk): tk.title("Layered Polygons") menubar = Menu(tk) menu_file = Menu(menubar, tearoff=0) menu_file.add_command(label="New...", command=self._new_scene) menu_file.add_command(label="Open...", command=self._open_scene) menu_file.add_separator() menu_file.add_command(label="Save", command=self._save_scene) menu_file.add_command(label="Save As...", command=self._save_scene_as) menu_file.add_separator() menu_export = Menu(menu_file, tearoff=0) menu_export.add_command(label="Wavefront (.obj)...", command=self._export_obj) menu_file.add_cascade(label="Export As", menu=menu_export) menu_file.add_separator() menu_file.add_command(label="Quit", command=self._quit_app) menubar.add_cascade(label="File", menu=menu_file) tk.config(menu=menubar) paned = PanedWindow(tk, relief=RAISED) paned.pack(fill=BOTH, expand=1) frame = Frame(paned) paned.add(frame) self._canvas = LayPolyCanvas(frame) bar_x = Scrollbar(frame, orient=HORIZONTAL) bar_x.pack(side=BOTTOM, fill=X) bar_x.config(command=self._canvas.xview) bar_y = Scrollbar(frame, orient=VERTICAL) bar_y.pack(side=RIGHT, fill=Y) bar_y.config(command=self._canvas.yview) self._canvas.config(xscrollcommand=bar_x.set, yscrollcommand=bar_y.set) self._canvas.pack(side=LEFT, expand=True, fill=BOTH) # Thanks to the two guys on Stack Overflow for that! # ( http://stackoverflow.com/a/7734187 ) self._layer_list = Listbox(paned, selectmode=SINGLE) paned.add(self._layer_list) self._scene = None self._current_layer = None self._is_drawing_polygon = False self._tk = tk self._canvas.bind("<Button-1>", self._canvas_left_click) self._canvas.bind("<Button-3>", self._canvas_right_click) self._canvas.bind("<Motion>", self._canvas_mouse_moved) self._layer_list.bind("<<ListboxSelect>>", self._layer_change) self._current_path = None def _canvas_left_click(self, event): if not self._scene or not self._current_layer: return x, y = self._canvas.window_to_canvas_coords(event.x, event.y) if self._is_drawing_polygon: polygon = self._current_layer.get_polygon_at(-1) # Move vtx away from mouse to not interfere with search for closest polygon.get_vertex_at(-1).\ set_coords(x-self.SNAP_RADIUS, y-self.SNAP_RADIUS) closest_vertex = self._current_layer.get_closest_vertex( x, y, self.SNAP_RADIUS) if closest_vertex: polygon.remove_vertex_at(-1) if closest_vertex is polygon.get_vertex_at(0): self._is_drawing_polygon = False else: polygon.add_vertex(closest_vertex) polygon.add_vertex(Vertex(x, y)) else: polygon.get_vertex_at(-1)\ .set_coords(x, y) polygon.add_vertex(Vertex(x, y)) self._canvas.notify_polygon_change( self._current_layer.get_polygon_count() - 1) else: # Create start vertex or use already existing one start_vertex = self._current_layer\ .get_closest_vertex(x, y, self.SNAP_RADIUS) if not start_vertex: start_vertex = Vertex(x, y) # Vertex for mouse cursor next_vertex = Vertex(x, y) self._current_layer.add_polygon( Polygon([start_vertex, next_vertex])) self._is_drawing_polygon = True self._canvas.notify_layer_change() def _canvas_right_click(self, event): if not self._current_layer: return if self._is_drawing_polygon: self._current_layer.remove_polygon_at(-1) self._is_drawing_polygon = False else: x, y = self._canvas.window_to_canvas_coords(event.x, event.y) for i in range(0, self._current_layer.get_polygon_count()): if self._current_layer.get_polygon_at(i).contains(x, y): self._current_layer.remove_polygon_at(i) break self._canvas.notify_layer_change() def _canvas_mouse_moved(self, event): if self._is_drawing_polygon: x, y = self._canvas.window_to_canvas_coords(event.x, event.y) self._current_layer.get_polygon_at(-1).get_vertex_at(-1)\ .set_coords(x, y) self._canvas.notify_polygon_change( self._current_layer.get_polygon_count() - 1) def _layer_change(self, event): selection = self._layer_list.curselection() if len(selection) > 0 and self._scene: layer = self._scene.get_layer_at(selection[0]) if layer: self._is_drawing_polygon = False self._current_layer = layer self._canvas.notify_new_layer(self._current_layer) def _set_scene(self, scene: Scene) -> bool: if scene.get_layer_count() <= 0: messagebox.showerror("Error!", "Scene has no layers!") return False self._scene = scene # Prepare canvas # TODO Extra 10px padding for canvas width, height = self._scene.get_size() self._canvas.config(scrollregion=(0, 0, width, height)) # Empty listbox, fill it, select first entry self._layer_list.delete(0, self._layer_list.size() - 1) for i in range(0, self._scene.get_layer_count()): self._layer_list.insert(i, self._scene.get_layer_at(i).get_name()) self._layer_list.selection_set(0) self._layer_list.event_generate("<<ListboxSelect>>") return True def _set_current_path(self, path): self._current_path = path if path: self._tk.title(path + " - Layered Polygons") else: self._tk.title("Untitled - Layered Polygons") def _new_scene(self): path = filedialog.askopenfilename(defaultextension=".ora", filetypes=[("OpenRaster files", ".ora")]) if not path: return scene = ora.read(path) if not scene: messagebox.showerror("Error!", "File could not be opened!") return if self._set_scene(scene): self._set_current_path(None) def _open_scene(self): path = filedialog.askopenfilename(defaultextension=".lp", filetypes=[("Layered polygons" " files", ".lp")]) if not path: return scene = lp.read(path) if not scene: messagebox.showerror("Error!", "File could not be opened!") return if self._set_scene(scene): self._set_current_path(path) def _save_scene_help(self, path): if lp.save(path, self._scene): self._set_current_path(path) else: messagebox.showerror("Error!", "File could not be saved!") def _save_scene(self): if not self._current_path: self._save_scene_as() return self._save_scene_help(self._current_path) def _save_scene_as(self): if not self._scene: return path = filedialog.asksaveasfilename(defaultextension=".lp", filetypes=[("Layered polygons" " files", ".lp")]) if path: self._save_scene_help(path) def _export_obj(self): if not self._scene: return path_obj = filedialog.asksaveasfilename(defaultextension=".obj", filetypes=[("Wavefront object" " files", ".obj")]) if not path_obj: return path_obj, path_mtl, path_data = obj.get_paths(path_obj) obj.save(path_obj, path_mtl, path_data, self._scene) def _quit_app(self): self._tk.quit() exit()
class DMselector(ttk.Frame): """Listbox for DM Creation, Selection, and Sorting.""" def __init__(self, master, conflict): """Initialize DM selection/creation Widget.""" ttk.Frame.__init__(self, master) self.conflict = conflict self.dms = conflict.decisionMakers # variables self.listVariable = StringVar( value=tuple(['Double Click to Add Item'])) self.selIdx = None self.selectedDM = None # widgets self.label = ttk.Label(self, text="Decision Makers") self.dmListDisp = Listbox(self, listvariable=self.listVariable) self.scrollY = ttk.Scrollbar(self, orient=VERTICAL, command=self.dmListDisp.yview) self.upBtn = ttk.Button(self, width=10, text='Up', command=self.upCmd) self.downBtn = ttk.Button(self, width=10, text='Down', command=self.downCmd) self.delBtn = ttk.Button(self, width=10, text='Delete', command=self.delCmd) # configuration self.dmListDisp.configure(yscrollcommand=self.scrollY.set) self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) self.label.grid(column=0, row=0, columnspan=5, sticky=NSEW) self.dmListDisp.grid(column=0, row=1, columnspan=5, sticky=NSEW) self.scrollY.grid(column=5, row=1, sticky=NSEW) self.upBtn.grid(column=2, row=2, sticky=NSEW) self.downBtn.grid(column=3, row=2, sticky=NSEW) self.delBtn.grid(column=4, row=2, sticky=NSEW) self.dmListDisp.bind('<<ListboxSelect>>', self.selChgCmd) self.dmListDisp.bind('<Double-1>', self.editCmd) self.dmListDisp.bind('<Delete>', self.delCmd) self.dmListDisp.bind('<Return>', self.editCmd) def refresh(self, event=None): """Refresh widget contents.""" self.updateList() for idx in range(len(self.dms)): self.dmListDisp.itemconfigure(idx, foreground='black') self.dmListDisp.itemconfigure(len(self.dms), foreground='#A0A0A0') def updateList(self, event=None): """Update the value in the displayed listVariable.""" self.listVariable.set( tuple(self.dms.names() + ['Double Click to Add Item'])) def moveEntry(self, idx, idx2): """Move an item from idx to idx2.""" self.dms.insert(idx2, self.dms.pop(idx)) self.updateList() self.dmListDisp.selection_clear(idx) self.dmListDisp.selection_set(idx2) self.selChgCmd() self.event_generate("<<dmOptChg>>") def upCmd(self, event=None): """Move the selected element up one space in the list.""" idx = self.selIdx # check that there is an entry selected, and it isn't the first entry. if idx not in [-1, 0, len(self.dms)]: self.moveEntry(idx, idx - 1) def downCmd(self, event=None): """Move the selected element down one space in the list.""" idx = self.selIdx # check that there is an entry selected, and it isn't the last entry if idx not in [-1, len(self.dms) - 1, len(self.dms)]: self.moveEntry(idx, idx + 1) def delCmd(self, *args): """Delete the selected element from the list.""" idx = self.selIdx if idx != len(self.dms): # check that a valid entry is selected del self.dms[idx] self.refresh() self.event_generate("<<dmOptChg>>") def selChgCmd(self, *args): """Called when the selection changes.""" self.selIdx = int(self.dmListDisp.curselection()[0]) if self.selIdx != len(self.conflict.decisionMakers): self.selectedDM = self.conflict.decisionMakers[self.selIdx] else: self.selectedDM = None self.event_generate('<<DMselected>>') def editCmd(self, *args): """Called when a list entry is selected for editing.""" self.event_generate('<<EditDM>>') def reselect(self, event=None): """Return selection focus to a dm after an action is completed.""" if self.selIdx is not None: self.dmListDisp.selection_set(self.selIdx) self.dmListDisp.event_generate('<<ListboxSelect>>')
class SettingsFrame(Frame): """ Frame inheritance class for application settings and controls. """ def __init__(self, app, *args, **kwargs): """ Constructor """ self.__app = app # Reference to main application class self.__master = self.__app.get_master() # Reference to root class (Tk) Frame.__init__(self, self.__master, *args, **kwargs) # Initialise up key variables self._image_num = 0 self._image_name = "image" self._settype = StringVar() self._zoom = DoubleVar() self._radius = DoubleVar() self._exponent = IntVar() self._zx_off = DoubleVar() self._zy_off = DoubleVar() self._cx_off = DoubleVar() self._cy_off = DoubleVar() self._maxiter = IntVar() self._zx_coord = DoubleVar() self._zy_coord = DoubleVar() self._theme = StringVar() self._shift = IntVar() self._themes = None self._filename = StringVar() self._filepath = None self._frames = IntVar() self._zoominc = DoubleVar() self._autoiter = IntVar() self._autosave = IntVar() self._validsettings = True self.body() def body(self): """ Set up frame and widgets. """ # Create settings panel widgets # pylint: disable=W0108 self.lbl_settype = Label(self, text=LBLMODE) self.spn_settype = Spinbox( self, values=("BurningShip", "Tricorn", "Julia", "Mandelbrot"), width=8, bg=GOOD, wrap=True, textvariable=self._settype, ) self.lbl_zoom = Label(self, text=LBLZOOM) self.ent_zoom = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._zoom, ) self.lbl_zoominc = Label(self, text=LBLZOOMINC, justify=LEFT) self.ent_zoominc = Entry(self, width=5, border=2, bg=GOOD, justify=RIGHT, textvariable=self._zoominc) self.lbl_zx_off = Label(self, text=LBLZXOFF) self.ent_zx_off = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._zx_off, ) self.lbl_zy_off = Label(self, text=LBLZYOFF) self.ent_zy_off = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._zy_off, ) self.lbl_cx_off = Label(self, text=LBLCX) self.ent_cx_off = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._cx_off, state=DISABLED, ) self.lbl_cy_off = Label(self, text=LBLCY) self.ent_cy_off = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._cy_off, state=DISABLED, ) self.lbl_niter = Label(self, text=LBLITER, justify=LEFT) self.ent_maxiter = Entry( self, border=2, relief="sunken", bg=GOOD, width=8, justify=RIGHT, textvariable=self._maxiter, ) self.chk_autoiter = Checkbutton(self, text="Auto", variable=self._autoiter, onvalue=1, offvalue=0) self.lbl_theme = Label(self, text=LBLTHEME, justify=LEFT) self.lbl_radius = Label(self, text=LBLRAD, justify=LEFT) self.ent_radius = Entry( self, border=2, relief="sunken", bg=GOOD, width=8, justify=RIGHT, textvariable=self._radius, ) self.lbl_exp = Label(self, text=LBLEXP) self.spn_exp = Spinbox( self, border=2, relief="sunken", bg=GOOD, width=4, from_=2, to=20, wrap=True, textvariable=self._exponent, ) self.sep_1 = ttk.Separator( self, orient=HORIZONTAL, ) self.lbx_theme = Listbox( self, border=2, relief="sunken", bg=GOOD, width=6, height=5, justify=LEFT, exportselection=False, ) self.lbl_shift = Label(self, text=LBLSHIFT, justify=LEFT) self.scl_shift = Scale( self, from_=0, to=100, orient=HORIZONTAL, variable=self._shift, border=2, relief="sunken", sliderlength=20, troughcolor=GOOD, ) self.scrollbar = Scrollbar(self, orient=VERTICAL) self.lbx_theme.config(yscrollcommand=self.scrollbar.set) self.scrollbar.config(command=self.lbx_theme.yview) self.lbx_theme.select_set(0) self.lbx_theme.event_generate("<<ListboxSelect>>") self.lbl_coords = Label(self, text="Re, Im", fg="grey") self.btn_plot = Button( self, text=BTNPLOT, width=8, fg="green", command=lambda: self.__app.frm_fractal.plot(), ) self.btn_cancel = Button( self, text=BTNCAN, width=8, command=lambda: self.__app.frm_fractal.cancel_press(), ) self.btn_reset = Button(self, text=BTNRST, width=8, command=self.reset) self.ent_save = Entry( self, textvariable=self._filename, width=6, border=2, relief="sunken", bg=GOOD, justify=LEFT, ) self._filename.set(self._image_name + str(self._image_num)) self.btn_save = Button(self, text=BTNSAVE, width=8, command=self.save_image) self.lbl_auto = Label(self, text=LBLAUTO, justify=LEFT) self.btn_autozoom = Button( self, text=BTNZOOM, width=8, command=lambda: self.__app.frm_fractal.animate_zoom(), ) self.btn_autospin = Button( self, text=BTNSPIN, width=8, command=lambda: self.__app.frm_fractal.animate_spin(), state=DISABLED, ) self.chk_autosave = Checkbutton(self, text=BTNSAVE, variable=self._autosave, onvalue=1, offvalue=0) self.lbl_frames = Label(self, text=FRMSTXT) self.ent_frames = Entry(self, width=5, border=2, bg=GOOD, justify=RIGHT, textvariable=self._frames) # Get list of available themes for idx, theme in enumerate(THEMES): self.lbx_theme.insert(idx, theme) self.body_arrange() # Position all widgets in frame self.reset() # Reset all settings to their defaults self.set_traces( ) # Trace entry variables for validation and event handling def body_arrange(self): """ Position widgets in frame """ # Position all widgets in their parent frames self.btn_plot.grid(column=0, row=1, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.btn_cancel.grid(column=1, row=1, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.btn_reset.grid(column=2, row=1, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.ent_save.grid(column=0, row=2, columnspan=2, sticky=(W, E), padx=3, pady=3) self.btn_save.grid(column=2, row=2, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.lbl_auto.grid(column=0, row=3, sticky=(W)) self.btn_autozoom.grid(column=1, row=3, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.btn_autospin.grid(column=2, row=3, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.lbl_frames.grid(column=0, row=4, sticky=(W)) self.ent_frames.grid(column=1, row=4, sticky=(W), padx=3, pady=3) self.chk_autosave.grid(column=2, row=4, sticky=(W), padx=3, pady=3) self.sep_1.grid(column=0, row=5, columnspan=3, pady=5, sticky=(W, E)) self.lbl_settype.grid(column=0, row=6, sticky=(W)) self.spn_settype.grid(column=1, row=6, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_zoom.grid(column=0, row=7, sticky=(W)) self.ent_zoom.grid(column=1, row=7, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_zoominc.grid(column=0, row=8, sticky=(W)) self.ent_zoominc.grid(column=1, row=8, sticky=(W), padx=3, pady=3) self.lbl_zx_off.grid(column=0, row=9, sticky=(W)) self.ent_zx_off.grid(column=1, row=9, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_zy_off.grid(column=0, row=10, sticky=(W)) self.ent_zy_off.grid(column=1, row=10, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_cx_off.grid(column=0, row=11, sticky=(W)) self.ent_cx_off.grid(column=1, row=11, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_cy_off.grid(column=0, row=12, sticky=(W)) self.ent_cy_off.grid(column=1, row=12, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_niter.grid(column=0, row=13, sticky=(W)) self.ent_maxiter.grid(column=1, row=13, sticky=(W), padx=3, pady=3) self.chk_autoiter.grid(column=2, row=13, sticky=(W), padx=3, pady=3) self.lbl_radius.grid(column=0, row=14, sticky=(W)) self.ent_radius.grid(column=1, row=14, sticky=(W), padx=3, pady=3) self.lbl_exp.grid(column=0, row=15, sticky=(W)) self.spn_exp.grid(column=1, row=15, sticky=(W), padx=3, pady=3) self.lbl_theme.grid(column=0, row=16, sticky=(W)) self.lbx_theme.grid(column=1, row=16, padx=3, pady=3, columnspan=2, sticky=(N, S, W, E)) self.scrollbar.grid(column=2, row=16, sticky=(N, S, E)) self.lbl_shift.grid(column=0, row=17, sticky=(W)) self.scl_shift.grid(column=1, row=17, columnspan=2, padx=3, pady=3, sticky=(W, E)) self.lbx_theme.bind("<<ListboxSelect>>", self.get_sel_theme) def set_traces(self): """ Set up entry variable traces for validation and event handling """ self._validsettings = True self._settype.trace("w", self.val_settings) self._zoom.trace("w", self.val_settings) self._zx_off.trace("w", self.val_settings) self._zy_off.trace("w", self.val_settings) self._cx_off.trace("w", self.val_settings) self._cy_off.trace("w", self.val_settings) self._maxiter.trace("w", self.val_settings) self._radius.trace("w", self.val_settings) self._exponent.trace("w", self.val_settings) self._filename.trace("w", self.val_settings) self._frames.trace("w", self.val_settings) self._zoominc.trace("w", self.val_settings) def val_settings(self, *args, **kwargs): """ Validate all user-entered settings. (A personal choice but I find this user experience more intuitive than the standard validatecommand method for Entry widgets) """ self._validsettings = True self.__app.set_status("") if self.is_float(self.ent_zoom.get()) and self._zoom.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_zoom, flg) if self.is_float(self.ent_zx_off.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_zx_off, flg) if self.is_float(self.ent_zy_off.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_zy_off, flg) if self.is_float(self.ent_cx_off.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_cx_off, flg) if self.is_float(self.ent_cy_off.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_cy_off, flg) if self.is_integer(self.ent_maxiter.get()) and self._maxiter.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_maxiter, flg) if self.is_float(self.ent_radius.get()) and self._radius.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_radius, flg) if self.is_integer(self.spn_exp.get()) and self._exponent.get() > 1: flg = GOOD else: flg = BAD self.flag_entry(self.spn_exp, flg) if self.is_integer(self.ent_frames.get()) and self._frames.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_frames, flg) if self.is_float(self.ent_zoominc.get()) and self._zoominc.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_zoominc, flg) if self.is_filename(self.ent_save.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_save, flg) if self.spn_settype.get() in {"Mandelbrot", "Tricorn", "BurningShip"}: self.btn_autospin.config(state=DISABLED) self.ent_cx_off.config(state=DISABLED) self.ent_cy_off.config(state=DISABLED) self._cx_off.set(0) self._cy_off.set(0) flg = GOOD elif self.spn_settype.get() == "Julia": self.btn_autospin.config(state=NORMAL) self.ent_cx_off.config(state=NORMAL) self.ent_cy_off.config(state=NORMAL) flg = GOOD else: flg = BAD self.flag_entry(self.spn_settype, flg) def flag_entry(self, ent, flag): """ Flag entry field as valid or invalid and set global validity status flag. This flag is used throughout to determine whether functions can proceed or not. """ ent.config(bg=flag) if flag == BAD: self._validsettings = False self.__app.set_status(VALERROR, "red") def is_float(self, flag): """ Validate if entry is a float. """ try: float(flag) return True except ValueError: return False def is_integer(self, flag): """ Validate if entry is a positive integer. """ try: int(flag) if int(flag) > 0: return True return False except ValueError: return False def is_filename(self, flag): """ Validate if entry represents a valid filename using a regexp. """ return match("^[\w\-. ]+$", flag) and flag != "" def reset(self): """ Reset settings to defaults. """ self._settype.set("Mandelbrot") self._zoom.set(0.75) self._zx_off.set(-0.5) self._zy_off.set(0.0) self._cx_off.set(0.0) self._cy_off.set(0.0) self._maxiter.set(128) self._radius.set(2.0) self._exponent.set(2) self._frames.set(10) self._zoominc.set(2.0) self._autoiter.set(1) self._autosave.set(0) self._theme.set("Default") self._filename.set("image0") self._shift.set(0) self.set_sel_theme() self.__app.set_status(SETINITTXT) def get_sel_theme(self, *args, **kwargs): """ Get selected theme from listbox and set global variable. """ idx = self.lbx_theme.curselection() if idx == "": idx = 0 self._theme.set(self.lbx_theme.get(idx)) def set_sel_theme(self): """ Lookup index of selected theme and highlight that listbox position. NB: this requires 'exportselection=False' option to be set on listbox to work properly. """ for idx, theme in enumerate(THEMES): if theme == self._theme.get(): self.lbx_theme.activate(idx) self.lbx_theme.see(idx) break def get_settings(self): """ Return all current settings as a dict. """ if not self._validsettings: settings = {"valid": self._validsettings} return settings settings = { "settype": self._settype.get(), "zoom": self._zoom.get(), "zxoffset": self._zx_off.get(), "zyoffset": self._zy_off.get(), "cxoffset": self._cx_off.get(), "cyoffset": self._cy_off.get(), "maxiter": self._maxiter.get(), "autoiter": self._autoiter.get(), "autosave": self._autosave.get(), "radius": self._radius.get(), "exponent": self._exponent.get(), "theme": self._theme.get(), "shift": self._shift.get(), "filepath": self._filepath, "filename": self._filename.get(), "frames": self._frames.get(), "zoominc": self._zoominc.get(), "valid": self._validsettings, } return settings def update_settings(self, **kwargs): """ Update settings from keyword parms. """ if "settype" in kwargs: self._settype.set(kwargs["settype"]) if "zoom" in kwargs: self._zoom.set(kwargs["zoom"]) if "zxoffset" in kwargs: self._zx_off.set(kwargs["zxoffset"]) if "zyoffset" in kwargs: self._zy_off.set(kwargs["zyoffset"]) if "cxoffset" in kwargs: self._cx_off.set(kwargs["cxoffset"]) if "cyoffset" in kwargs: self._cy_off.set(kwargs["cyoffset"]) if "maxiter" in kwargs: self._maxiter.set(kwargs["maxiter"]) if "autoiter" in kwargs: self._autoiter.set(kwargs["autoiter"]) if "autosave" in kwargs: self._autosave.set(kwargs["autosave"]) if "radius" in kwargs: self._radius.set(kwargs["radius"]) if "exponent" in kwargs: self._exponent.set(kwargs["exponent"]) if "filepath" in kwargs: self._filepath.set(kwargs["filepath"]) if "filename" in kwargs: self._filename.set(kwargs["filename"]) if "frames" in kwargs: self._frames.set(kwargs["frames"]) if "zoominc" in kwargs: self._zoominc.set(kwargs["zoominc"]) if "theme" in kwargs: self._theme.set(kwargs["theme"]) self.set_sel_theme() if "shift" in kwargs: self._shift.set(kwargs["shift"]) def set_filepath(self): """ Sets filepath for saved files for the duration of this session. """ default = os.getcwd() # Default _filepath is current working directory if self._filepath is None: self._filepath = filedialog.askdirectory(title=SAVETITLE, initialdir=default, mustexist=True) if self._filepath == "": self._filepath = None # User cancelled return self._filepath def save_image(self): """ Save image as PNG file to selected filepath and automatically increment default image name. NB: currently this will overwrite any existing file of the same name without warning. """ # Bug out if the settings are invalid settings = self.__app.frm_settings.get_settings() if not settings["valid"]: return # Check if image has been created image = self.__app.frm_fractal.mandelbrot.get_image() if image is None: self.__app.set_status(NOIMGERROR, "red") return # Set _filename and path if self.set_filepath() is None: # User cancelled return fname = self._filename.get() fqname = self._filepath + "/" + fname # Save the image along with its metadata try: # image.write(fqname + ".png", format="png") image.save(fqname + ".png", format="png") self.save_metadata() except OSError: self.__app.set_status(SAVEERROR, "red") self._filepath = None return self._image_num += 1 self._filename.set(self._image_name + str(self._image_num)) self.__app.set_status(IMGSAVETXT + fqname + ".png", "green") # Return focus to image frame self.__app.frm_fractal.focus_set() def save_metadata(self): """ Save json file containing meta data associated with image, allowing it to be imported and reproduced. """ if self._filepath is None: if self.set_filepath() is None: # User cancelled return fname = self._filename.get() fqname = self._filepath + "/" + fname filename = fqname + ".json" createtime = strftime("%b %d %Y %H:%M:%S %Z", gmtime()) jsondata = { MODULENAME: { "filename": fqname + ".png", "created": createtime, "settype": self._settype.get(), "zoom": self._zoom.get(), "zoominc": self._zoominc.get(), "frames": self._frames.get(), "escradius": self._radius.get(), "exponent": self._exponent.get(), "maxiter": self._maxiter.get(), "zxoffset": self._zx_off.get(), "zyoffset": self._zy_off.get(), "cxoffset": self._cx_off.get(), "cyoffset": self._cy_off.get(), "theme": self._theme.get(), "shift": self._shift.get(), } } try: with open(filename, "w") as outfile: dump(jsondata, outfile) except OSError: self.__app.set_status(METASAVEERROR, "red") self._filepath = None # Return focus to image frame self.__app.frm_fractal.focus_set() def import_metadata(self): """ Update settings from imported json metadata file. """ # Select and read file try: default = os.getcwd() filepath = filedialog.askopenfilename( initialdir=default, title=SELTITLE, filetypes=(("json files", "*.json"), ("all files", "*.*")), ) if filepath == "": # User cancelled return with open(filepath, "r") as infile: jsondata = infile.read() except OSError: self.__app.set_status(OPENFILEERROR, "red") return # Parse file try: settings = loads(jsondata).get(MODULENAME) # Set plot parameters self._settype.set(settings.get("settype", "Mandelbrot")) self._zoom.set(settings.get("zoom", 1)) self._zoominc.set(settings.get("zoominc", 2.0)) self._frames.set(settings.get("frames", 10)) self._radius.set(settings.get("escradius", 2.0)) self._exponent.set(settings.get("exponent", 2)) self._maxiter.set(settings.get("maxiter", 256)) self._zx_off.set(settings.get("zxoffset", 0)) self._zy_off.set(settings.get("zyoffset", 0)) self._cx_off.set(settings.get("cxoffset", 0)) self._cy_off.set(settings.get("cyoffset", 0)) self._theme.set(settings.get("theme", "Default")) self._shift.set(settings.get("shift", 0)) except OSError: self.__app.set_status(BADJSONERROR, "red") return self.set_sel_theme() fbase = os.path.basename(filepath) filename, fileext = os.path.splitext(fbase) self.__app.set_status( "Metadata file " + filename + fileext + METAPROMPTTXT, "green") # Return focus to image frame self.__app.frm_fractal.focus_set()
def openCalculator(handler): global conversionresulttext, fromCurrencyInput, toCurrencyInput, amountInput, calculatorWindow currencyapi = get("https://api.exchangeratesapi.io/latest", data=None) if currencyapi.status_code == 200: #Check for reponse exchangerates = loads(currencyapi.content.decode()) exchangerates["rates"]["DUCO"] = float(ducofiat) calculatorWindow = Toplevel() calculatorWindow.resizable(False, False) calculatorWindow.title("Duino-Coin Wallet - Calculator") calculatorWindow.configure(background = backgroundColor) calculatorWindow.transient([root]) textFont2 = Font(calculatorWindow, size=12,weight="bold") textFont3 = Font(calculatorWindow, size=14,weight="bold") textFont = Font(calculatorWindow, size=12,weight="normal") Label(calculatorWindow, text="CURRENCY CONVERTER", background = backgroundColor, foreground = foregroundColor, font = textFont3).grid(row=0, column=0) Label(calculatorWindow, text="FROM", background = backgroundColor, foreground = foregroundColor, font = textFont2).grid(row=1, column=0) fromCurrencyInput = Listbox(calculatorWindow, exportselection=False, background = backgroundColor, foreground = foregroundColor, selectbackground = "#7bed9f", border="0", font=textFont, width="20", height="13") fromCurrencyInput.grid(row=2, column=0) i=0 for currency in exchangerates["rates"]: fromCurrencyInput.insert(i, currency) i = i+1 Label(calculatorWindow, text="TO", background = backgroundColor, foreground = foregroundColor, font = textFont2).grid(row=1, column=1) toCurrencyInput = Listbox(calculatorWindow, exportselection=False, foreground = foregroundColor, background = backgroundColor, selectbackground = "#7bed9f", border="0", font=textFont, width="20", height="13") toCurrencyInput.grid(row=2, column=1) i=0 for currency in exchangerates["rates"]: toCurrencyInput.insert(i, currency) i = i+1 toCurrencyInput.select_set(0) toCurrencyInput.event_generate("<<ListboxSelect>>") fromCurrencyInput.select_set(32) fromCurrencyInput.event_generate("<<ListboxSelect>>") Label(calculatorWindow, text="AMOUNT", background = backgroundColor, foreground = foregroundColor, font = textFont2).grid(row=3, column=0) def clear_ccamount_placeholder(self): amountInput.delete("0", "100") amountInput = Entry(calculatorWindow, background = "#7bed9f", foreground=foregroundColor, border="0", font=textFont, width="20") amountInput.grid(row=4, column=0) amountInput.insert("0", str(getBalance())) amountInput.bind("<FocusIn>", clear_ccamount_placeholder) Button(calculatorWindow, text="Convert", background = "#FEEEDA", foreground=foregroundColor, command=currencyConvert, width="22").grid(row=3, column=1, pady=(5, 0)) conversionresulttext = StringVar(calculatorWindow) conversionresulttext.set("RESULT: 0.0") conversionresultLabel = Label(calculatorWindow, textvariable=conversionresulttext, background = backgroundColor, foreground = foregroundColor, font = textFont2) conversionresultLabel.grid(row=4, column=1) Label(calculatorWindow, text=" ", background = backgroundColor, foreground = foregroundColor, font = textFont2).grid(row=5, column=0) calculatorWindow.mainloop()
class AlbumsPanel(Frame): def __init__(self, parent, albumsController: IAlbumsController): super().__init__(parent) self.albumsController = albumsController self.albumList: List[AlbumDb] = [] self.selectedAlbumName: str = None self.musicFromDisc: List[Music] = [] self.displayedMusicOnComputer = None self.displayedMusicInAlbum = None self.__initView() def __initView(self): self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(3, weight=1) self.albumsListBox = Listbox(self, exportselection=0) self.albumsListBox.grid(row=0, column=0, sticky='WE', padx=(10, 0), pady=10) self.albumsListBox.bind('<Double-1>', self.__albumDoubleClicked) self.albumsListBox.bind("<<ListboxSelect>>", self.__albumSelected) albumsScrollbar = Scrollbar(self, command=self.albumsListBox.yview) albumsScrollbar.grid(row=0, column=1, sticky='NS', padx=(0, 10), pady=10) self.albumsListBox.configure(yscrollcommand=albumsScrollbar.set) ###################################################################################################### buttonsFrame = Frame(self) buttonsFrame.grid(row=1, column=0, sticky='W', padx=10) self.albumDeleteButton = Button(buttonsFrame, text=ALBUM_DELETE_TEXT, command=self.__deleteAlbum) self.albumDeleteButton.grid(row=0, column=0, padx=5) self.albumExportButton = Button(buttonsFrame, text=ALBUM_EXPORT_TEXT, command=self.__exportAlbumClicked) self.albumExportButton.grid(row=0, column=1, padx=5) self.albumImportButton = Button(buttonsFrame, text=ALBUM_IMPORT_TEXT, command=self.__importAlbumClicked) self.albumImportButton.grid(row=0, column=2, padx=5) ###################################################################################################### createFrame = Frame(self) createFrame.grid(row=2, column=0, sticky='W', padx=10) label = Label(createFrame, text=ALBUM_CREATE_LABEL_TEXT) label.grid(row=0, column=0) self.createAlbumNameEntry = Entry(createFrame) self.createAlbumNameEntry.grid(row=0, column=1) self.createAlbumButton = Button(createFrame, text=ALBUM_CREATE_BUTTON_TEXT, command=self.__createAlbum) self.createAlbumButton.grid(row=0, column=2) ###################################################################################################### # Music on computer musicFrame = Frame(self, padx=10, pady=10) musicFrame.grid(row=3, column=0, columnspan=2, sticky='WENS') musicFrame.grid_columnconfigure(0, weight=1) musicFrame.grid_columnconfigure(3, weight=1) musicFrame.grid_rowconfigure(1, weight=1) label = Label(musicFrame, text=MUSIC_ON_COMPUTER_TEXT) label.grid(row=0, column=0, sticky='W') self.musicOnComputerListBox = Listbox(musicFrame) self.musicOnComputerListBox.grid(row=1, column=0, sticky='WENS') musicOnComputerScrollbar = Scrollbar(musicFrame, command=self.musicOnComputerListBox.yview) musicOnComputerScrollbar.grid(row=1, column=1, sticky='NS') self.musicOnComputerListBox.configure(yscrollcommand=musicOnComputerScrollbar.set) ###################################################################################################### buttonFrame = Frame(musicFrame, padx=10) buttonFrame.grid(row=1, column=2) self.removeFromAlbumButton = Button(buttonFrame, text='<<', command=self.__removeMusicFromAlbumClicked) self.removeFromAlbumButton.grid(row=0, column=0) self.addToAlbumButton = Button(buttonFrame, text='>>', command=self.__addMusicToAlbumClicked) self.addToAlbumButton.grid(row=1, column=0) ###################################################################################################### #Music in album label = Label(musicFrame, text=MUSIC_IN_ALBUM_TEXT) label.grid(row=0, column=3, sticky='W') self.musicInAlbumListBox = Listbox(musicFrame) self.musicInAlbumListBox.grid(row=1, column=3, sticky='WENS') musicInAlbumScrollbar = Scrollbar(musicFrame, command=self.musicInAlbumListBox.yview) musicInAlbumScrollbar.grid(row=1, column=4, sticky='NS') self.musicInAlbumListBox.configure(yscrollcommand=musicInAlbumScrollbar.set) ###################################################################################################### def displayAlbums(self, albums: List[AlbumDb], musicFromDisc: List[Music]): self.albumList = albums self.musicFromDisc = musicFromDisc self.albumsListBox.delete(0, 'end') for i in range(len(self.albumList)): a = self.albumList[i] self.albumsListBox.insert(i, a.name) if self.selectedAlbumName is not None: selectedIndex = -1 for i in range(len(self.albumList)): if self.albumList[i].name == self.selectedAlbumName: selectedIndex = i break if selectedIndex >= 0: self.albumsListBox.select_set(selectedIndex) self.albumsListBox.event_generate("<<ListboxSelect>>") def __deleteAlbum(self): selection = self.albumsListBox.curselection() if len(selection) <= 0: messagebox.showwarning('Warning', DELETE_ALBUM_SELECT_TEXT) return self.albumsListBox.selection_clear(0, END) self.albumsListBox.event_generate("<<ListboxSelect>>") album = self.albumList[selection[0]] self.albumsController.deleteAlbum(album.name) def __createAlbum(self): name = self.createAlbumNameEntry.get() if name is None or name == '': messagebox.showwarning('Warning', CRATE_ALBUM_INSERT_NAME_TEXT) return self.albumsController.createAlbum(name) def __albumSelected(self, event): self.musicOnComputerListBox.delete(0, 'end') self.musicInAlbumListBox.delete(0, 'end') selection = self.albumsListBox.curselection() if len(selection) <= 0: return selectedAlbum = self.albumList[selection[0]] self.selectedAlbumName = selectedAlbum.name self.__lisMusicOnComputer(selectedAlbum) self.__lisMusicInAlbum(selectedAlbum) def __lisMusicOnComputer(self, album): musicInAlbum = set() for m in album.music: musicInAlbum.add(m.path) if self.musicFromDisc is None: return music = self.musicFromDisc.copy() music = filter(lambda m: m.path not in musicInAlbum, music) self.displayedMusicOnComputer = list(music) i = 0 for m in self.displayedMusicOnComputer: self.musicOnComputerListBox.insert(i, m.filename) i = i + 1 def __lisMusicInAlbum(self, album): musicOnDisc = set() if self.musicFromDisc is not None: for m in self.musicFromDisc: musicOnDisc.add(m.path) self.displayedMusicInAlbum = list(album.music) i = 0 for m in self.displayedMusicInAlbum: description = os.path.basename(m.path) if m.path not in musicOnDisc: description = '<missing> ' + m.path self.musicInAlbumListBox.insert(i, description) i = i + 1 def __addMusicToAlbumClicked(self): selection = self.albumsListBox.curselection() if len(selection) <= 0: return selectedAlbum = self.albumList[selection[0]] selection = self.musicOnComputerListBox.curselection() if len(selection) <= 0: messagebox.showwarning('Warning', ADD_TO_ALBUM_SELECT_TEXT) return selectedMusic = self.displayedMusicOnComputer[selection[0]] self.albumsController.addMusicToAlbum(selectedAlbum.name, selectedMusic.path) def __removeMusicFromAlbumClicked(self): selection = self.albumsListBox.curselection() if len(selection) <= 0: return selectedAlbum = self.albumList[selection[0]] selection = self.musicInAlbumListBox.curselection() if len(selection) <= 0: messagebox.showwarning('Warning', REMOVE_FROM_ALBUM_SELECT_TEXT) return selectedMusic = self.displayedMusicInAlbum[selection[0]] self.albumsController.removeMusicFromAlbum(selectedAlbum.name, selectedMusic.path) def __exportAlbumClicked(self): selection = self.albumsListBox.curselection() if len(selection) <= 0: messagebox.showwarning('Warning', EXPORT_ALBUM_SELECT_TEXT) return selectedAlbum = self.albumList[selection[0]] self.albumsController.exportAlbum(selectedAlbum.name) def __importAlbumClicked(self): self.albumsController.importAlbum() def __albumDoubleClicked(self, event): album = self.albumList[self.albumsListBox.curselection()[0]] self.albumsController.addAlbumToQueue(album)
class PiScreen(tkinter.Frame): def __init__(self, master: 'tkinter.Tk'): global client, status, theme # host = '192.168.1.120' host = 'localhost' if sys.platform.startswith('linux'): host = 'localhost' client.connect(host, 6600) tkinter.Frame.__init__(self, master, padx=0, pady=0) self.pack() self.place(height=240, width=320, x=0, y=0) status = client.status() self.volume = int(status["volume"]) self.screen_data = { "1": [ "QUEUE", "PLAYLISTS", "LIBRARY", "SETUP", "CLEAR PLAYLIST", "RANDOM " + status['random'], "REPEAT " + status['repeat'], "SINGLE " + status['single'], "CONSUME " + status['consume'] ], "1.1": { "ACTION": "QUEUE" }, "1.2": { "ACTION": "PLAYLISTS" }, "1.3": ["ARTISTS", "ALBUMS", "GENRES"], "1.3.1": { "ACTION": "ARTISTS" }, "1.3.2": { "ACTION": "ALBUMS" }, "1.3.3": { "ACTION": "GENRES" }, "1.4": ["UPDATE LIBRARY", "THEMES"], "1.4.1": { "ACTION": "UPDATE_LIBRARY" }, "1.4.2": { "ACTION": "THEMES" }, "1.5": { "ACTION": "CLEAR" }, "1.6": { "ACTION": "RANDOM" }, "1.7": { "ACTION": "REPEAT" }, "1.8": { "ACTION": "SINGLE" }, "1.9": { "ACTION": "CONSUME" } } self.screen_format = {"1.Q": "SONG", "1.P": "PLAYLIST"} self.current_song_var = tkinter.StringVar() self.footer_text_var = tkinter.StringVar() # Screens self.playerScreen = Canvas(self, width=320, height=240, bg=theme['PLAYER']['background'], borderwidth=0, highlightthickness=0) self.menuScreen = Frame(self, width=320, height=240, bg="white") self.menuScreen.place(height=240, width=320, x=0, y=0) # Menu Screen items self.headerFrame = Frame(self.menuScreen, width=320, height=20, bg=theme['HEADER']['background']) self.headerFrame.pack(side=tkinter.TOP, fill=X) self.currentSongLabel = Label(self.headerFrame, font=(theme['HEADER']['font'], 12, 'bold'), bg=theme['HEADER']['background'], foreground=theme['HEADER']['foreground'], textvariable=self.current_song_var, justify=tkinter.LEFT, anchor=tkinter.W) self.currentSongLabel.place(x=0, y=0, width=300, height=20, anchor=tkinter.NW) self.volumeLabel = Label(self.headerFrame, font=(theme['HEADER']['font'], 10, 'bold'), bg=theme['HEADER']['background'], foreground=theme['HEADER']['foreground'], text='') self.volumeLabel.place(x=300, y=0, anchor=tkinter.NW) self.mainFrame = Frame(self.menuScreen, width=320, height=200) self.mainFrame.pack(side=tkinter.TOP, fill=Y) self.listbox = Listbox(self.mainFrame, selectmode=tkinter.SINGLE, font=(theme['MAIN']['font'], 11), bg=theme['MAIN']['background'], fg=theme['MAIN']['foreground'], height=10, activestyle="none", borderwidth=0, highlightthickness=0, selectbackground=theme['MAIN']['selected'], selectforeground=theme['MAIN']['foreground']) self.listbox.bind("<Key>", self.handle_keys) self.listbox.configure(width=320, height=11) self.listbox.pack(side=tkinter.TOP, expand=1, ipadx=0, ipady=0, padx=0, pady=0) self.listbox.focus_set() self.footer = Label(self.menuScreen, textvariable=self.footer_text_var, font=(theme['FOOTER']['font'], 10, 'bold'), bg=theme['FOOTER']['background'], foreground=theme['FOOTER']['foreground'], justify=tkinter.LEFT, anchor=tkinter.W) self.footer.configure(width=320, height=1) self.footer.pack(side=tkinter.BOTTOM) self.focus_set() self.bind("<Key>", self.handle_keys) self.screen = "1" self.show_screen() self.tick() def tick(self): global awayCount, keyMode, footerMessage, footerMessageCount self.update_header() if keyMode != 'PLAYER': awayCount += 1 if awayCount > 120: awayCount = 0 self.screen = '' self.show_screen() else: awayCount = 0 if footerMessage == self.footer_text_var.get(): footerMessageCount += 1 if footerMessageCount > 8: footerMessageCount = 0 self.footer_text_var.set("") else: footerMessage = self.footer_text_var.get() footerMessageCount = 0 self.after(800, self.tick) def update_header(self): global status, keyMode, songChanged, currentSong, songName, songTicker, minTickerLength, songTickerCount status = client.status() self.volume = int(status["volume"]) self.volumeLabel.configure(text=status["volume"]) if status["state"] == "play": currentSong = client.currentsong() song = currentSong["artist"] + " - " + currentSong["title"] if songName != song: songChanged = True songName = song if keyMode != 'PLAYER': # song changed, refresh ui if len(songName) >= minTickerLength: songTicker = True songTickerCount = -1 else: songTicker = False songTickerCount = 0 if keyMode != 'PLAYER': if songTicker: songTickerCount += 1 if songTickerCount == len(songName) + 5: songTickerCount = 0 song = songName + " " new_song = song[songTickerCount:] + song[:songTickerCount] self.current_song_var.set(new_song) elif keyMode == 'PLAYER': self.show_player() else: if songName != '': self.current_song_var.set('') songName = '' songChanged = True if keyMode == 'PLAYER': self.show_player() def show_screen(self): global keyMode if self.screen == '': keyMode = 'PLAYER' self.menuScreen.place_forget() self.playerScreen.place(height=240, width=320, x=0, y=0) self.show_player() self.update() self.screen = '1' return self.listbox.delete(0, self.listbox.size() - 1) format_name = "string" if self.screen in self.screen_format: format_name = self.screen_format[self.screen] if isinstance(self.screen_data[self.screen], list): for item in self.screen_data[self.screen]: if format_name == "string": if not item: self.listbox.insert(tkinter.END, "") else: self.listbox.insert(tkinter.END, item[:36]) if format_name == "SONG": songname = '' if 'artist' in item: songname = item['artist'][:18] songname += " - " if 'title' in item: max = 36 - len(songname) songname += item['title'][:max] self.listbox.insert(tkinter.END, songname) if format_name == "PLAYLIST": playlist_name = '' if isinstance(item, str): playlist_name = item else: playlist_name = item['playlist'] self.listbox.insert(tkinter.END, playlist_name) self.listbox.select_set(0) # This only sets focus on the first item. self.listbox.event_generate("<<ListboxSelect>>") self.update() return def show_player(self): global image, bg, songChanged, volumeChanged if songChanged or image is None: if sys.platform.startswith('linux'): process = subprocess.Popen( "./coverart.sh", shell=True, stdout=subprocess.PIPE).stdout.read() else: process = "./icons/ic_album_white_48dp.png" image = ImageTk.PhotoImage( Image.open(process).resize((136, 136), Image.ANTIALIAS)) if bg is None: process = "./icons/bg.png" if 'img_background' in theme['PLAYER']: process = theme['PLAYER']['img_background'] bg = ImageTk.PhotoImage( Image.open(process).resize((320, 240), Image.ANTIALIAS)) if icon_random is None: self.load_icons() if status["state"] == "play": if songChanged: self.playerScreen.delete(tkinter.ALL) self.playerScreen.create_image(160, 120, image=bg) self.playerScreen.create_rectangle( 10, 10, 150, 150, fill=theme['PLAYER']['foreground']) self.playerScreen.create_image(80, 80, image=image) self.playerScreen.create_image(178, 132, image=icon_random) self.playerScreen.create_image(224, 132, image=icon_repeat) self.playerScreen.create_image(270, 132, image=icon_single) self.playerScreen.create_rectangle( 298, 146, 308, 92, fill=theme['PLAYER']['background'], outline=theme['PLAYER']['foreground'], width=1) self.playerScreen.create_line( 303, 144, 303, 144 - int(self.volume / 2), fill=theme['PLAYER']['foreground'], width=7) self.playerScreen.create_text( 10, 160, text=currentSong['artist'], anchor=tkinter.NW, fill=theme['PLAYER']['foreground'], font=(theme['PLAYER']['font'], 14, 'bold')) self.playerScreen.create_text( 10, 185, text=currentSong['title'], anchor=tkinter.NW, fill=theme['PLAYER']['foreground'], font=(theme['PLAYER']['font'], 12, 'bold')) self.playerScreen.create_text( 10, 210, text=currentSong['album'], anchor=tkinter.NW, fill=theme['PLAYER']['foreground'], font=(theme['PLAYER']['font'], 10, 'bold')) else: time = str(status['time']).split(":") played = int((float(time[0]) / float(time[1])) * 320) if played < 3: # bug self.playerScreen.create_rectangle( 0, 236, 320, 240, fill=theme['PLAYER']['background']) self.playerScreen.create_rectangle( 0, 236, played, 240, fill=theme['PLAYER']['foreground']) if volumeChanged: volumeChanged = False self.playerScreen.create_rectangle( 298, 146, 308, 92, fill=theme['PLAYER']['background'], outline=theme['PLAYER']['foreground'], width=1) self.playerScreen.create_line( 303, 144, 303, 144 - int(self.volume / 2), fill=theme['PLAYER']['foreground'], width=7) else: # Blank Screen self.playerScreen.delete(tkinter.ALL) self.playerScreen.create_image(160, 120, image=bg) self.playerScreen.create_text( 20, 20, text=theme['PLAYER']['default_message'], anchor=tkinter.NW, fill=theme['PLAYER']['foreground'], font=(theme['PLAYER']['font'], 20, 'bold')) songChanged = False return def handle_keys(self, event): global config, client, selectedAlbum, selectedArtist, selectedGenre global keyMode, textEntry, textBackAction, textSaveAction, awayCount, theme_name global albums, artists, queue, songs, playlists, status, genres, songChanged, volumeChanged awayCount = 0 keycode = str(event.keycode) # self.footer_text_var.set(str("Key Pressed : "+keycode)) if keyMode == 'PLAYER' and keycode != config["PISCREEN_KEYS"]["vol_up"] \ and keycode != config["PISCREEN_KEYS"]["vol_down"] \ and keycode != config["PISCREEN_KEYS"]["play"] \ and keycode != config["PISCREEN_KEYS"]["next"] \ and keycode != config["PISCREEN_KEYS"]["prev"] \ and keycode != config["PISCREEN_KEYS"]["power"] \ and keycode != config["PISCREEN_KEYS"]["left"]: keyMode = 'MENU' self.playerScreen.place_forget() self.menuScreen.place(height=240, width=320, x=0, y=0) self.show_screen() self.update() return if keyMode == 'TEXT': if keycode == config["PISCREEN_KEYS"]["back"]: # back keyMode = 'MENU' self.run_command(textBackAction) if keycode == config["PISCREEN_KEYS"]["ok"]: # ok keyMode = 'MENU' self.run_command(textSaveAction) if event.keysym in '0123456789-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': textEntry += event.keysym self.footer_text_var.set(str("Entry : " + textEntry)) return # self.footer.configure(text=str('Key Pressed ' + str(event.keycode))) if keycode == config["PISCREEN_KEYS"]["menu"]: if self.screen == "1.P": selection = int(self.listbox.curselection()[0]) + 1 if selection > 1: self.footer_text_var.set( str("Press 1 + OK to Delete Playlist")) keyMode = 'TEXT' textBackAction = "PLAYLISTS" textSaveAction = "DELETE_PLAYLIST" if self.screen == "1.Q": self.footer_text_var.set(str("Press OK to remove Song")) keyMode = 'TEXT' textBackAction = "QUEUE" textSaveAction = "DELETE_SONG" return if keycode == config["PISCREEN_KEYS"]["down"]: # down if self.listbox.size() > 0: selection = int(self.listbox.curselection()[0]) count = self.listbox.size() if selection < (count - 1): self.listbox.select_clear(selection) self.listbox.selection_set(selection + 1) self.listbox.event_generate("<<ListboxSelect>>") return if keycode == config["PISCREEN_KEYS"]["up"]: # up if self.listbox.size() > 0: selection = int(self.listbox.curselection()[0]) if selection > 0: self.listbox.select_clear(selection) self.listbox.selection_set(selection - 1) self.listbox.event_generate("<<ListboxSelect>>") return if keycode == config["PISCREEN_KEYS"]["left"] or keycode == config[ "PISCREEN_KEYS"]["back"]: # left or escape if self.screen != "1": menu = self.screen.rsplit(".", maxsplit=1) new_screen = menu[0] self.screen = new_screen self.show_screen() else: self.screen = '' songChanged = True self.show_screen() return if keycode == config["PISCREEN_KEYS"]["right"] or keycode == config[ "PISCREEN_KEYS"]["ok"]: # right or return if self.listbox.size() > 0: selection = int(self.listbox.curselection()[0]) + 1 new_screen = self.screen + "." + str(selection) if new_screen in self.screen_data: if type(self.screen_data[new_screen]) is list: self.screen = new_screen self.show_screen() else: self.run_command( self.screen_data[new_screen]["ACTION"]) else: if str(new_screen).startswith("1.Q."): menu = new_screen.rsplit(".", maxsplit=1) client.playid(int(queue[int(menu[1]) - 1]["id"])) return if str(new_screen).startswith("1.P."): menu = new_screen.rsplit(".", maxsplit=1) if menu[1] == "1": keyMode = 'TEXT' textBackAction = 'PLAYLISTS' textSaveAction = 'SAVE_PLAYLIST' textEntry = '' self.footer_text_var.set( 'Back to Cancel, Ok to Save') else: playlist = playlists[int(menu[1]) - 1]['playlist'] client.clear() client.load(playlist) client.play() return if str(new_screen).startswith("1.3.A"): if new_screen.count(".") == 3: menu = new_screen.rsplit(".", maxsplit=1) selectedArtist = artists[int(menu[1]) - 1] albums = [] albums = client.list("album", selectedArtist) albums[:0] = ["Add All"] self.footer_text_var.set("SELECTED Artist " + selectedArtist) self.screen = new_screen self.screen_data[new_screen] = albums self.show_screen() return elif new_screen.count(".") == 4: menu = new_screen.rsplit(".", maxsplit=1) if menu[1] == "1": # add all client.findadd("artist", selectedArtist) self.footer_text_var.set("Added All for " + selectedArtist) self.screen = menu[0].rsplit(".", maxsplit=1)[0] self.show_screen() else: selectedAlbum = albums[int(menu[1]) - 1] songs = client.list("title", "album", selectedAlbum, "artist", selectedArtist) songs[:0] = ["Add All"] self.screen = new_screen self.screen_data[new_screen] = songs self.show_screen() self.footer_text_var.set("Album Selected " + selectedAlbum) return elif new_screen.count(".") == 5: menu = new_screen.rsplit(".", maxsplit=1) if menu[1] == "1": # add all client.findadd("album", selectedAlbum, "artist", selectedArtist) self.footer_text_var.set("Added All for " + selectedAlbum + "/" + selectedArtist) self.screen = menu[0].rsplit(".", maxsplit=1)[0] self.show_screen() else: selected_song = songs[int(menu[1]) - 1] client.findadd("title", selected_song, "album", selectedAlbum, "artist", selectedArtist) self.footer_text_var.set("Added " + selected_song + "/" + selectedAlbum + "/" + selectedArtist) return if str(new_screen).startswith("1.3.B"): menu = new_screen.rsplit(".", maxsplit=1) if new_screen.count(".") == 3: selectedAlbum = albums[int(menu[1]) - 1] songs = client.list("title", "album", selectedAlbum) songs[:0] = ["Add All"] self.screen = new_screen self.screen_data[new_screen] = songs self.show_screen() self.footer_text_var.set("Album Selected " + selectedAlbum) if new_screen.count(".") == 4: if menu[1] == "1": # add all client.findadd("album", selectedAlbum) self.footer_text_var.set( "Added All for album " + selectedAlbum) self.screen = menu[0].rsplit(".", maxsplit=1)[0] self.show_screen() else: selected_song = songs[int(menu[1]) - 1] client.findadd("title", selected_song, "album", selectedAlbum) self.footer_text_var.set("Added " + selected_song + "/" + selectedAlbum) return if str(new_screen).startswith("1.3.C"): menu = new_screen.rsplit(".", maxsplit=1) if new_screen.count(".") == 3: selectedGenre = genres[int(menu[1]) - 1] songs = client.list("title", "genre", selectedGenre) self.screen = new_screen self.screen_data[new_screen] = songs self.show_screen() self.footer_text_var.set("Genre Selected " + selectedAlbum) if new_screen.count(".") == 4: selected_song = songs[int(menu[1]) - 1] client.findadd("title", selected_song, "genre", selectedGenre) self.footer_text_var.set("Added " + selected_song + selectedGenre) return if str(new_screen).startswith("1.4.T"): menu = new_screen.rsplit(".", maxsplit=1) theme_name = themes[int(menu[1]) - 1] self.footer_text_var.set("Applying Theme " + theme_name) self.apply_theme() return if keycode == config["PISCREEN_KEYS"]["vol_up"]: if self.volume < 100: self.volume += 1 client.setvol(self.volume) volumeChanged = True self.footer_text_var.set("Volume Up") else: self.footer_text_var.set("Volume Max!!") return if keycode == config["PISCREEN_KEYS"]["vol_down"]: if self.volume > 0: self.volume -= 1 client.setvol(self.volume) volumeChanged = True self.footer_text_var.set("Volume Down") else: self.footer_text_var.set("Volume Zero!!") return if keycode == config["PISCREEN_KEYS"]["play"]: if status["state"] == "play": client.pause() self.footer_text_var.set("Paused") else: client.play() self.footer_text_var.set("Playing") return if keycode == config["PISCREEN_KEYS"]["next"]: client.next() self.footer_text_var.set("Next Song") return if keycode == config["PISCREEN_KEYS"]["prev"]: client.previous() self.footer_text_var.set("Previous Song") return if keycode == config["PISCREEN_KEYS"]["home"]: self.screen = '' self.show_screen() return if keycode == config["PISCREEN_KEYS"]["power"]: if sys.platform.startswith('linux'): call("sudo nohup shutdown -h now", shell=True) else: self.footer_text_var.set("Can't PowerOff from remote") return self.footer_text_var.set("UNKNOWN " + keycode) def run_command(self, action): global client, keyMode, textEntry, status global albums, artists, queue, songs, playlists, genres, themes if action == "QUEUE": local_queue = client.playlistinfo() queue.clear() for item in local_queue: queue.append(item) self.screen = "1.Q" self.screen_data["1.Q"] = queue self.footer_text_var.set("Right to play Song, Menu to delete") self.show_screen() elif action == "PLAYLISTS": playlists = client.listplaylists() playlists[:0] = ["SAVE PLAYLIST"] self.screen = "1.P" self.screen_data["1.P"] = playlists self.footer_text_var.set("Right to play Playlist, Menu to delete") self.show_screen() elif action == "ARTISTS": artists = client.list("artist") self.screen = "1.3.A" self.screen_data["1.3.A"] = artists self.show_screen() elif action == "ALBUMS": albums = client.list("album") self.screen = "1.3.B" self.screen_data["1.3.B"] = albums self.show_screen() elif action == "GENRES": genres = client.list("genre") self.screen = "1.3.C" self.screen_data["1.3.C"] = genres self.show_screen() elif action == "UPDATE_LIBRARY": self.footer_text_var.set("Updating library") client.update() elif action == "THEMES": self.footer_text_var.set("Select Theme") themes = ["default", "foofighters", "light"] self.screen = "1.4.T" self.screen_data["1.4.T"] = themes self.show_screen() elif action == "SAVE_PLAYLIST": keyMode = 'MENU' found = False if textEntry == '': self.footer_text_var.set("Name Empty!!") return for playlist in playlists: if isinstance( playlist, str) is False and textEntry == playlist['playlist']: found = True if found: client.rm(textEntry) client.save(textEntry) else: client.save(textEntry) self.footer_text_var.set("Saved Playlist " + textEntry) textEntry = '' self.run_command("PLAYLISTS") elif action == "DELETE_PLAYLIST": keyMode = 'MENU' if textEntry == '1': selection = int(self.listbox.curselection()[0]) client.rm(playlists[selection]['playlist']) textEntry = '' self.run_command("PLAYLISTS") elif action == "DELETE_SONG": keyMode = 'MENU' client.delete(int(self.listbox.curselection()[0])) textEntry = '' self.run_command("QUEUE") elif action == "CLEAR": self.footer_text_var.set("Clearing Queue") client.clear() elif action == "RANDOM": if status['random'] == '0': client.random('1') else: client.random('0') status = client.status() self.screen_data['1'][5] = "RANDOM " + status['random'] self.update_random() self.show_screen() elif action == "REPEAT": if status['repeat'] == '0': client.repeat('1') else: client.repeat('0') status = client.status() self.screen_data['1'][6] = "REPEAT " + status['repeat'] self.update_repeat() self.show_screen() elif action == "SINGLE": if status['single'] == '0': client.single('1') else: client.single('0') status = client.status() self.screen_data['1'][7] = "SINGLE " + status['single'] self.update_single() self.show_screen() elif action == "CONSUME": if status['consume'] == '0': client.consume('1') else: client.consume('0') status = client.status() self.screen_data['1'][8] = "CONSUME " + status['consume'] self.show_screen() self.update() return def load_icons(self): self.update_random() self.update_repeat() self.update_single() def update_random(self): global status, theme, icon_random fgcolor = ImageColor.getrgb(theme['PLAYER']['foreground']) bgcolor = ImageColor.getrgb(theme['PLAYER']['background']) fgcolor += (255, ) bgcolor += (255, ) icon_random = Image.open('./icons/ic_shuffle_white_36dp.png') if icon_random.mode != 'RGBA': icon_random = icon_random.convert('RGBA') data = list(icon_random.getdata()) newData = list() for pixel in data: if pixel[3] != 0: if status['random'] == '1': newData.append(fgcolor) else: newData.append(bgcolor) else: newData.append(pixel) icon_random.putdata(newData) icon_random = ImageTk.PhotoImage( icon_random.resize((36, 36), Image.ANTIALIAS)) def update_single(self): global status, theme, icon_single fgcolor = ImageColor.getrgb(theme['PLAYER']['foreground']) bgcolor = ImageColor.getrgb(theme['PLAYER']['background']) fgcolor += (255, ) bgcolor += (255, ) icon_single = Image.open('./icons/ic_repeat_one_white_36dp.png') if icon_single.mode != 'RGBA': icon_single = icon_single.convert('RGBA') data = list(icon_single.getdata()) newData = list() for pixel in data: if pixel[3] != 0: if status['single'] == '1': newData.append(fgcolor) else: newData.append(bgcolor) else: newData.append(pixel) icon_single.putdata(newData) icon_single = ImageTk.PhotoImage( icon_single.resize((36, 36), Image.ANTIALIAS)) def update_repeat(self): global status, theme, icon_repeat fgcolor = ImageColor.getrgb(theme['PLAYER']['foreground']) bgcolor = ImageColor.getrgb(theme['PLAYER']['background']) fgcolor += (255, ) bgcolor += (255, ) icon_repeat = Image.open('./icons/ic_repeat_white_36dp.png') if icon_repeat.mode != 'RGBA': icon_repeat = icon_repeat.convert('RGBA') data = list(icon_repeat.getdata()) newData = list() for pixel in data: if pixel[3] != 0: if status['repeat'] == '1': newData.append(fgcolor) else: newData.append(bgcolor) else: newData.append(pixel) icon_repeat.putdata(newData) icon_repeat = ImageTk.PhotoImage( icon_repeat.resize((36, 36), Image.ANTIALIAS)) def apply_theme(self): global theme_name, theme, config, bg my_file = Path('./theme/' + theme_name + '/theme.ini') if my_file.is_file(): theme = configparser.ConfigParser() theme.read('./theme/' + theme_name + '/theme.ini') # player related settings bg = None self.playerScreen.configure(bg=theme['PLAYER']['background']) self.load_icons() # menu related settings self.headerFrame.configure(bg=theme['HEADER']['background']) self.currentSongLabel.configure( font=(theme['HEADER']['font'], 12, 'bold'), bg=theme['HEADER']['background'], foreground=theme['HEADER']['foreground']) self.volumeLabel.configure( font=(theme['HEADER']['font'], 10, 'bold'), bg=theme['HEADER']['background'], foreground=theme['HEADER']['foreground']) self.listbox.configure( font=(theme['MAIN']['font'], 11), bg=theme['MAIN']['background'], fg=theme['MAIN']['foreground'], selectbackground=theme['MAIN']['selected'], selectforeground=theme['MAIN']['foreground']) self.footer.configure(font=(theme['FOOTER']['font'], 10, 'bold'), bg=theme['FOOTER']['background'], foreground=theme['FOOTER']['foreground']) # write theme to config.ini config["THEME"]["theme"] = theme_name with open('config.ini', 'w') as configfile: config.write(configfile) else: self.footer_text_var.set("Theme Not Found") theme_name = config["THEME"]["theme"]
def openStats(handler): statsApi = get( "https://raw.githubusercontent.com/revoxhere/duco-statistics/master/api.json", data=None) if statsApi.status_code == 200: #Check for reponse statsApi = (statsApi.json()) print(statsApi) statsWindow = Toplevel() statsWindow.resizable(False, False) statsWindow.title("Duino-Coin Wallet - Stats") statsWindow.configure(background=backgroundColor) statsWindow.transient([root]) textFont3 = Font(statsWindow, size=14, weight="bold") textFont = Font(statsWindow, size=12, weight="normal") Label(statsWindow, text="Duco Statistics", background=backgroundColor, foreground=foregroundColor, font=textFont3).grid(row=0, column=0) i = 3 i2 = 3 for key in statsApi.keys(): if str(key) == 'Active workers' or str( key) == 'Top 10 richest miners' or str( key) == 'Total supply' or str( key) == 'Full last block hash' or str( key) == 'GitHub API file update count' or str( key) == 'Diff increases per': pass else: if len(statsApi.get(str(key))) > 8: Label(statsWindow, text=f"{key}: {statsApi.get(str(key))}", background=backgroundColor, foreground=foregroundColor, font=textFont).grid(row=i2, column=1, sticky=W) i2 += 1 else: Label(statsWindow, text=f"{key}: {statsApi.get(str(key))}", background=backgroundColor, foreground=foregroundColor, font=textFont).grid(row=i, column=0, sticky=W) i += 1 Active_workers_listbox = Listbox(statsWindow, exportselection=False, background=backgroundColor, foreground=foregroundColor, selectbackground="#7bed9f", border="0", font=textFont, width="20", height="13") Active_workers_listbox.grid(row=1, column=0, sticky=W) i = 0 for worker in (statsApi['Active workers']).split(', '): Active_workers_listbox.insert(i, worker) i = i + 1 Active_workers_listbox.select_set(32) Active_workers_listbox.event_generate("<<ListboxSelect>>") Top_10_listbox = Listbox(statsWindow, exportselection=False, background=backgroundColor, foreground=foregroundColor, selectbackground="#7bed9f", border="0", font=textFont, width="33", height="13") Top_10_listbox.grid(row=1, column=1, sticky=W) i = 0 for rich in (statsApi['Top 10 richest miners']).split(', '): Top_10_listbox.insert(i, rich) i = i + 1 Top_10_listbox.select_set(32) Top_10_listbox.event_generate("<<ListboxSelect>>") statsWindow.mainloop()
class MusicPanel(Frame): def __init__(self, parent, musicController: IMusicController): super().__init__(parent) self.musicController = musicController self.musicList: List[Music] = [] self.selectedMusicPath: str = None self.__initView() def __initView(self): self.grid_columnconfigure(0, weight=1) self.musicListBox = Listbox(self) self.musicListBox.grid(row=0, column=0, sticky='WE', padx=(10, 0), pady=10) self.musicListBox.bind('<Double-1>', self.musicDoubleClicked) self.musicListBox.bind("<<ListboxSelect>>", self.musicSelected) musicScrollbar = Scrollbar(self, command=self.musicListBox.yview) musicScrollbar.grid(row=0, column=1, sticky='NS', padx=(0, 10), pady=10) self.musicListBox.configure(yscrollcommand=musicScrollbar.set) ################################################################################ skipLabel = Label(self, text=SKIP_TEXT) skipLabel.grid(row=1, column=0) self.skipListBox = Listbox(self, exportselection=0) self.skipListBox.grid(row=2, column=0, sticky='WE', padx=(10, 0), pady=10) skipScrollbar = Scrollbar(self, command=self.skipListBox.yview) skipScrollbar.grid(row=2, column=1, sticky='NS', padx=(0, 10), pady=10) self.skipListBox.configure(yscrollcommand=skipScrollbar.set) self.deleteSkipButton = Button(self, text=DELETE_SKIP_TEXT, command=self.onSkipDelete) self.deleteSkipButton.grid(row=3, column=0, sticky='W', padx=10) ################################################################################ self.addSkipFrame = Frame(self) self.addSkipFrame.grid(row=4, column=0, padx=10, sticky='W') self.addSkipLabel = Label(self.addSkipFrame, text=ADD_SKIP_LABEL_TEXT) self.addSkipLabel.grid(row=0, column=0) self.addSkipStartMinutesTE = Entry(self.addSkipFrame, width=5, exportselection=0) self.addSkipStartMinutesTE.insert(END, '0') self.addSkipStartMinutesTE.grid(row=0, column=1) label = Label(self.addSkipFrame, text=':') label.grid(row=0, column=2) self.addSkipStartSecondsTE = Entry(self.addSkipFrame, width=5, exportselection=0) self.addSkipStartSecondsTE.insert(END, '0') self.addSkipStartSecondsTE.grid(row=0, column=3) label = Label(self.addSkipFrame, text=ADD_SKIP_TO_LABEL_TEXT) label.grid(row=0, column=4) self.addSkipEndMinutesTE = Entry(self.addSkipFrame, width=5, exportselection=0) self.addSkipEndMinutesTE.insert(END, '0') self.addSkipEndMinutesTE.grid(row=0, column=5) label = Label(self.addSkipFrame, text=':') label.grid(row=0, column=6) self.addSkipEndSecondsTE = Entry(self.addSkipFrame, width=5, exportselection=0) self.addSkipEndSecondsTE.insert(END, '0') self.addSkipEndSecondsTE.grid(row=0, column=7) self.addSkipButton = Button(self.addSkipFrame, text=ADD_SKIP_BUTTON_TEXT, command=self.__addSkip) self.addSkipButton.grid(row=0, column=8, padx=2) def displayMusic(self, music: List[Music]): self.musicList = music self.musicListBox.delete(0, 'end') for i in range(len(music)): m = music[i] self.musicListBox.insert(i, m) if self.selectedMusicPath is not None: selectedIndex = -1 for i in range(len(self.musicList)): if self.musicList[i].path == self.selectedMusicPath: selectedIndex = i break if selectedIndex >= 0: self.musicListBox.select_set(selectedIndex) self.musicListBox.event_generate("<<ListboxSelect>>") def musicDoubleClicked(self, event): music = self.musicList[self.musicListBox.curselection()[0]] self.musicController.onMusicDoubleClicked(music) def onSkipDelete(self): musicSelection = self.musicListBox.curselection() skipSelection = self.skipListBox.curselection() if len(skipSelection) <= 0 or len(musicSelection) <= 0: messagebox.showwarning('Warning', DELETE_SKIP_SELECT_MUSIC) return music = self.musicList[musicSelection[0]] self.musicController.onSkipDeleteClicked(music.skips[skipSelection[0]]) def musicSelected(self, event): self.skipListBox.delete(0, 'end') selection = self.musicListBox.curselection() if len(selection) <= 0: return selectedMusic = self.musicList[selection[0]] self.selectedMusicPath = selectedMusic.path if selectedMusic.skips is not None: for i in range(len(selectedMusic.skips)): s = selectedMusic.skips[i] startMinute = int(s.start / 60) startSecond = s.start % 60 stopMinute = int(s.end / 60) stopSecond = s.end % 60 text = f'Skip from {startMinute:02d}:{startSecond:02d} to {stopMinute:02d}:{stopSecond:02d}' self.skipListBox.insert(i, text) def __addSkip(self): selection = self.musicListBox.curselection() if len(selection) <= 0: messagebox.showwarning('Warning', ADD_SKIP_SELECT_MUSIC) return music = self.musicList[selection[0]] try: startMinutes = int(self.addSkipStartMinutesTE.get()) startSeconds = int(self.addSkipStartSecondsTE.get()) endMinutes = int(self.addSkipEndMinutesTE.get()) endSeconds = int(self.addSkipEndSecondsTE.get()) except: messagebox.showwarning('Warning', ADD_SKIP_PARSE_ERROR_TEXT) return start = startMinutes * 60 + startSeconds end = endMinutes * 60 + endSeconds if end <= start: messagebox.showwarning('Warning', ADD_SKIP_END_LOWER_THAN_START) return self.musicController.onSkipAddClicked(music.path, start, end)
class index_select(Frame): def __init__(self,controller,current_model,master,*args,**kwargs): self.controller = controller self.current_model = current_model self.filtered_list = None self.saved_selection = None from tkinter import EXTENDED,Scrollbar,Y #dibujar widget super().__init__(master, *args, **kwargs) f_st=Frame(self) Label(f_st,text="Current_index: ").pack() Label(f_st, text="Current_filter: ").pack() f_st.pack() frame_index_listbox = Frame(self) self.listbox = Listbox(frame_index_listbox, exportselection=False,selectmode=EXTENDED) self.listbox.pack(side=LEFT) scrollbar = Scrollbar(frame_index_listbox) scrollbar.pack(side=LEFT, fill=Y) frame_index_listbox.pack() # attach listbox to scrollbar self.listbox.config(yscrollcommand=scrollbar.set) scrollbar.config(command=self.listbox.yview) f=Frame(self) Label(f,text="Filtro: ").pack(side=LEFT) self.entry_w = Entry(f) self.entry_w.pack(side=LEFT) f.pack() f2=Frame(self) Button(f2, text='Filter',command=self.filter_indexs).pack(side=LEFT) Button(f2,text='Clean Filter',command=self.clean_filter).pack(side=LEFT) Button(f2, text='Export sel for gen',command=self.export_selection).pack(side=LEFT) f2.pack() f3=Frame(self) Button(self, text='<<',command=self.next_prev(-1)).pack(side=LEFT) Button(self, text='>>',command=self.next_prev(1)).pack(side=LEFT) f3.pack() self.update_model(current_model) self.listbox.select_set(0) self.listbox.event_generate("<<ListboxSelect>>") self.listbox.bind('<<ListboxSelect>>', self.selection) def filter_indexs(self): import random path_filter = self.entry_w.get() with open(path_filter,'r') as f: indexs_list_str = f.read().strip() random.seed(3) indexs_list = list(set(indexs_list_str.split('\n'))) print(len(indexs_list)) random.shuffle(indexs_list) self.filtered_list = indexs_list self.listbox.delete(0, END) for item in indexs_list: self.listbox.insert(END, item) def clean_filter(self): self.filtered_list = None self.update_model(self.current_model) self.listbox.delete(0, END) for item in sorted(self.index_list): self.listbox.insert(END, item) self.saved_selection = None def export_selection(self): sel_files_folder = os.path.join('config_files','select_files') os.makedirs(sel_files_folder,exist_ok=True) sel_list = self.listbox.curselection() index_list = [self.listbox.get(ind) for ind in sel_list] print("Exporting list for image generator. List: {0}".format(index_list)) now_s = now_string() out_selection_file = {'index_list' : index_list, 'train_result_path': self.current_model.current_config_file, 'details' : '', 'mask_file' : self.current_model.current_mask_file} sel_file_name = "{0}_{1}_{2}_selection.json".format(self.current_model.classifier_key,self.current_model.dataset_key,now_s) sel_path = os.path.join(sel_files_folder,sel_file_name) with open(sel_path,'w') as f: json.dump(out_selection_file,f) print("Select in {0}".format(sel_path)) def update_model(self, current_model): self.model = current_model self.index_list = self.model.get_index_list() self.current_index = self.model.get_current_index() self.mask_list = self.model.get_current_mask_index_list() indexs_list = self.filtered_list if self.filtered_list else sorted(self.index_list) self.listbox.delete(0, END) for item in indexs_list: self.listbox.insert(END, item) if self.saved_selection: ind,ypos = self.saved_selection self.listbox.selection_set(ind) self.listbox.yview_moveto(ypos[0]) # go back to that position def next_prev(self,x): def selection(): ind_l = self.index_list.index(self.current_index) n = len(self.index_list) n_ind_l = (ind_l+x) % n next = self.index_list[n_ind_l] self.current_index = next self.listbox.selection_clear(0, END) self.listbox.select_set(n_ind_l) self.controller.event_change_index(next) return selection def selection(self,event): w = event.widget index = int(w.curselection()[0]) value = w.get(index) print("v: {0}".format(value)) selected_index = value self.current_index = selected_index self.controller.event_change_index(selected_index) self.saved_selection = (index,self.listbox.yview()) pass
class AutoCompleteEntryListbox(Frame): def __init__(self, master=None, completevalues=[], allow_other_values=False, **kwargs): """ Create a Entry + Listbox with autocompletion. Keyword arguments: - allow_other_values (boolean): whether the user is allowed to enter values not in the list """ exportselection = kwargs.pop('exportselection', False) width = kwargs.pop('width', None) justify = kwargs.pop('justify', None) font = kwargs.pop('font', None) Frame.__init__(self, master, padding=4, **kwargs) self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) self._allow_other_values = allow_other_values self._completevalues = completevalues self._validate = self.register(self.validate) self.entry = Entry(self, width=width, justify=justify, font=font, validate='key', exportselection=exportselection, validatecommand=(self._validate, "%d", "%S", "%i", "%s", "%P")) f = Frame(self, style='border.TFrame', padding=1) self.listbox = Listbox(f, width=width, justify=justify, font=font, exportselection=exportselection, selectmode="browse", highlightthickness=0, relief='flat') self.listbox.pack(fill='both', expand=True) scroll = AutoHideScrollbar(self, orient='vertical', command=self.listbox.yview) self.listbox.configure(yscrollcommand=scroll.set) self.entry.grid(sticky='ew') f.grid(sticky='nsew') scroll.grid(row=1, column=1, sticky='ns') for c in self._completevalues: self.listbox.insert('end', c) self.listbox.bind('<<ListboxSelect>>', self.update_entry) self.listbox.bind("<KeyPress>", self.keypress) self.entry.bind("<Tab>", self.tab) self.entry.bind("<Down>", self.down) self.entry.bind("<Up>", self.up) self.entry.focus_set() def tab(self, event): """Move at the end of selected text on tab press.""" self.entry = event.widget self.entry.selection_clear() self.entry.icursor("end") return "break" def keypress(self, event): """Select the first item which name begin by the key pressed.""" key = event.char.lower() l = [i for i in self._completevalues if i[0].lower() == key] if l: i = self._completevalues.index(l[0]) self.listbox.selection_clear(0, "end") self.listbox.selection_set(i) self.listbox.see(i) self.update_entry() def up(self, event): """Navigate in the listbox with up key.""" try: i = self.listbox.curselection()[0] self.listbox.selection_clear(0, "end") if i <= 0: i = len(self._completevalues) self.listbox.see(i - 1) self.listbox.select_set(i - 1) except (TclError, IndexError): self.listbox.selection_clear(0, "end") i = len(self._completevalues) self.listbox.see(i - 1) self.listbox.select_set(i - 1) self.listbox.event_generate('<<ListboxSelect>>') def down(self, event): """Navigate in the listbox with down key.""" try: i = self.listbox.curselection()[0] self.listbox.selection_clear(0, "end") if i >= len(self._completevalues): i = -1 self.listbox.see(i + 1) self.listbox.select_set(i + 1) except (TclError, IndexError): self.listbox.selection_clear(0, "end") self.listbox.see(0) self.listbox.select_set(0) self.listbox.event_generate('<<ListboxSelect>>') def validate(self, action, modif, pos, prev_txt, new_txt): """Complete the text in the entry with values.""" try: sel = self.entry.selection_get() txt = prev_txt.replace(sel, '') except TclError: txt = prev_txt if action == "0": txt = txt[:int(pos)] + txt[int(pos) + 1:] return True else: txt = txt[:int(pos)] + modif + txt[int(pos):] l = [i for i in self._completevalues if i[:len(txt)] == txt] if l: i = self._completevalues.index(l[0]) self.listbox.selection_clear(0, "end") self.listbox.selection_set(i) self.listbox.see(i) index = self.entry.index("insert") self.entry.delete(0, "end") self.entry.insert(0, l[0].replace("\ ", " ")) self.entry.selection_range(index + 1, "end") self.entry.icursor(index + 1) return True else: return self._allow_other_values def __getitem__(self, key): return self.cget(key) def update_entry(self, event=None): """Update entry when an item is selected in the listbox.""" try: sel = self.listbox.get(self.listbox.curselection()[0]) except (TclError, IndexError): return self.entry.delete(0, "end") self.entry.insert(0, sel) self.entry.selection_clear() self.entry.icursor("end") self.event_generate('<<ItemSelect>>') def keys(self): keys = Combobox.keys(self) keys.append('allow_other_values') return keys def get(self): return self.entry.get() def cget(self, key): if key == 'allow_other_values': return self._allow_other_values elif key == 'completevalues': return self._completevalues else: return self.cget(self, key) def config(self, dic={}, **kwargs): self.configure(dic={}, **kwargs) def configure(self, dic={}, **kwargs): dic2 = {} dic2.update(dic) dic2.update(kwargs) self._allow_other_values = dic2.pop('allow_other_values', self._allow_other_values) self._completevalues = dic2.pop('completevalues', self._completevalues) self.config(self, dic2)
class FontChooser(Toplevel): """Font chooser dialog.""" def __init__(self, master, font_dict={}, text="Abcd", title="Font Chooser", **kwargs): """ Create a new FontChooser instance. Arguments: master : Tk or Toplevel instance master window font_dict : dict dictionnary, like the one returned by the ``actual`` method of a ``Font`` object: :: {'family': str, 'size': int, 'weight': 'bold'/'normal', 'slant': 'italic'/'roman', 'underline': bool, 'overstrike': bool} text : str text to be displayed in the preview label title : str window title kwargs : dict additional keyword arguments to be passed to ``Toplevel.__init__`` """ Toplevel.__init__(self, master, **kwargs) self.title(title) self.resizable(False, False) self.protocol("WM_DELETE_WINDOW", self.quit) self._validate_family = self.register(self.validate_font_family) self._validate_size = self.register(self.validate_font_size) # --- variable storing the chosen font self.res = "" style = Style(self) style.configure("prev.TLabel", background="white") bg = style.lookup("TLabel", "background") self.configure(bg=bg) # --- family list self.fonts = list(set(families())) self.fonts.append("TkDefaultFont") self.fonts.sort() for i in range(len(self.fonts)): self.fonts[i] = self.fonts[i].replace(" ", "\ ") max_length = int(2.5 * max([len(font) for font in self.fonts])) // 3 self.sizes = ["%i" % i for i in (list(range(6, 17)) + list(range(18, 32, 2)))] # --- font default font_dict["weight"] = font_dict.get("weight", "normal") font_dict["slant"] = font_dict.get("slant", "roman") font_dict["underline"] = font_dict.get("underline", False) font_dict["overstrike"] = font_dict.get("overstrike", False) font_dict["family"] = font_dict.get("family", self.fonts[0].replace('\ ', ' ')) font_dict["size"] = font_dict.get("size", 10) # --- creation of the widgets # ------ style parameters (bold, italic ...) options_frame = Frame(self, relief='groove', borderwidth=2) self.font_family = StringVar(self, " ".join(self.fonts)) self.font_size = StringVar(self, " ".join(self.sizes)) self.var_bold = BooleanVar(self, font_dict["weight"] == "bold") b_bold = Checkbutton(options_frame, text=TR["Bold"], command=self.toggle_bold, variable=self.var_bold) b_bold.grid(row=0, sticky="w", padx=4, pady=(4, 2)) self.var_italic = BooleanVar(self, font_dict["slant"] == "italic") b_italic = Checkbutton(options_frame, text=TR["Italic"], command=self.toggle_italic, variable=self.var_italic) b_italic.grid(row=1, sticky="w", padx=4, pady=2) self.var_underline = BooleanVar(self, font_dict["underline"]) b_underline = Checkbutton(options_frame, text=TR["Underline"], command=self.toggle_underline, variable=self.var_underline) b_underline.grid(row=2, sticky="w", padx=4, pady=2) self.var_overstrike = BooleanVar(self, font_dict["overstrike"]) b_overstrike = Checkbutton(options_frame, text=TR["Overstrike"], variable=self.var_overstrike, command=self.toggle_overstrike) b_overstrike.grid(row=3, sticky="w", padx=4, pady=(2, 4)) # ------ Size and family self.var_size = StringVar(self) self.entry_family = Entry(self, width=max_length, validate="key", validatecommand=(self._validate_family, "%d", "%S", "%i", "%s", "%V")) self.entry_size = Entry(self, width=4, validate="key", textvariable=self.var_size, validatecommand=(self._validate_size, "%d", "%P", "%V")) self.list_family = Listbox(self, selectmode="browse", listvariable=self.font_family, highlightthickness=0, exportselection=False, width=max_length) self.list_size = Listbox(self, selectmode="browse", listvariable=self.font_size, highlightthickness=0, exportselection=False, width=4) scroll_family = Scrollbar(self, orient='vertical', command=self.list_family.yview) scroll_size = Scrollbar(self, orient='vertical', command=self.list_size.yview) self.preview_font = Font(self, **font_dict) if len(text) > 30: text = text[:30] self.preview = Label(self, relief="groove", style="prev.TLabel", text=text, font=self.preview_font, anchor="center") # --- widget configuration self.list_family.configure(yscrollcommand=scroll_family.set) self.list_size.configure(yscrollcommand=scroll_size.set) self.entry_family.insert(0, font_dict["family"]) self.entry_family.selection_clear() self.entry_family.icursor("end") self.entry_size.insert(0, font_dict["size"]) try: i = self.fonts.index(self.entry_family.get().replace(" ", "\ ")) except ValueError: # unknown font i = 0 self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) self.list_family.see(i) try: i = self.sizes.index(self.entry_size.get()) self.list_size.selection_clear(0, "end") self.list_size.selection_set(i) self.list_size.see(i) except ValueError: # size not in list pass self.entry_family.grid(row=0, column=0, sticky="ew", pady=(10, 1), padx=(10, 0)) self.entry_size.grid(row=0, column=2, sticky="ew", pady=(10, 1), padx=(10, 0)) self.list_family.grid(row=1, column=0, sticky="nsew", pady=(1, 10), padx=(10, 0)) self.list_size.grid(row=1, column=2, sticky="nsew", pady=(1, 10), padx=(10, 0)) scroll_family.grid(row=1, column=1, sticky='ns', pady=(1, 10)) scroll_size.grid(row=1, column=3, sticky='ns', pady=(1, 10)) options_frame.grid(row=0, column=4, rowspan=2, padx=10, pady=10, ipadx=10) self.preview.grid(row=2, column=0, columnspan=5, sticky="eswn", padx=10, pady=(0, 10), ipadx=4, ipady=4) button_frame = Frame(self) button_frame.grid(row=3, column=0, columnspan=5, pady=(0, 10), padx=10) Button(button_frame, text="Ok", command=self.ok).grid(row=0, column=0, padx=4, sticky='ew') Button(button_frame, text=TR["Cancel"], command=self.quit).grid(row=0, column=1, padx=4, sticky='ew') self.list_family.bind('<<ListboxSelect>>', self.update_entry_family) self.list_size.bind('<<ListboxSelect>>', self.update_entry_size, add=True) self.list_family.bind("<KeyPress>", self.keypress) self.entry_family.bind("<Return>", self.change_font_family) self.entry_family.bind("<Tab>", self.tab) self.entry_size.bind("<Return>", self.change_font_size) self.entry_family.bind("<Down>", self.down_family) self.entry_size.bind("<Down>", self.down_size) self.entry_family.bind("<Up>", self.up_family) self.entry_size.bind("<Up>", self.up_size) # bind Ctrl+A to select all instead of go to beginning self.bind_class("TEntry", "<Control-a>", self.select_all) self.wait_visibility(self) self.grab_set() self.entry_family.focus_set() self.lift() def select_all(self, event): """Select all entry content.""" event.widget.selection_range(0, "end") def keypress(self, event): """Select the first font whose name begin by the key pressed.""" key = event.char.lower() l = [i for i in self.fonts if i[0].lower() == key] if l: i = self.fonts.index(l[0]) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) self.list_family.see(i) self.update_entry_family() def up_family(self, event): """Navigate in the family listbox with up key.""" try: i = self.list_family.curselection()[0] self.list_family.selection_clear(0, "end") if i <= 0: i = len(self.fonts) self.list_family.see(i - 1) self.list_family.select_set(i - 1) except TclError: self.list_family.selection_clear(0, "end") i = len(self.fonts) self.list_family.see(i - 1) self.list_family.select_set(i - 1) self.list_family.event_generate('<<ListboxSelect>>') def up_size(self, event): """Navigate in the size listbox with up key.""" try: s = self.var_size.get() if s in self.sizes: i = self.sizes.index(s) elif s: sizes = list(self.sizes) sizes.append(s) sizes.sort(key=lambda x: int(x)) i = sizes.index(s) else: i = 0 self.list_size.selection_clear(0, "end") if i <= 0: i = len(self.sizes) self.list_size.see(i - 1) self.list_size.select_set(i - 1) except TclError: i = len(self.sizes) self.list_size.see(i - 1) self.list_size.select_set(i - 1) self.list_size.event_generate('<<ListboxSelect>>') def down_family(self, event): """Navigate in the family listbox with down key.""" try: i = self.list_family.curselection()[0] self.list_family.selection_clear(0, "end") if i >= len(self.fonts): i = -1 self.list_family.see(i + 1) self.list_family.select_set(i + 1) except TclError: self.list_family.selection_clear(0, "end") self.list_family.see(0) self.list_family.select_set(0) self.list_family.event_generate('<<ListboxSelect>>') def down_size(self, event): """Navigate in the size listbox with down key.""" try: s = self.var_size.get() if s in self.sizes: i = self.sizes.index(s) elif s: sizes = list(self.sizes) sizes.append(s) sizes.sort(key=lambda x: int(x)) i = sizes.index(s) - 1 else: s = len(self.sizes) - 1 self.list_size.selection_clear(0, "end") if i < len(self.sizes) - 1: self.list_size.selection_set(i + 1) self.list_size.see(i + 1) else: self.list_size.see(0) self.list_size.select_set(0) except TclError: self.list_size.selection_set(0) self.list_size.event_generate('<<ListboxSelect>>') def toggle_bold(self): """Update font preview weight.""" b = self.var_bold.get() self.preview_font.configure(weight=["normal", "bold"][b]) def toggle_italic(self): """Update font preview slant.""" b = self.var_italic.get() self.preview_font.configure(slant=["roman", "italic"][b]) def toggle_underline(self): """Update font preview underline.""" b = self.var_underline.get() self.preview_font.configure(underline=b) def toggle_overstrike(self): """Update font preview overstrike.""" b = self.var_overstrike.get() self.preview_font.configure(overstrike=b) def change_font_family(self, event=None): """Update font preview family.""" family = self.entry_family.get() if family.replace(" ", "\ ") in self.fonts: self.preview_font.configure(family=family) def change_font_size(self, event=None): """Update font preview size.""" size = int(self.var_size.get()) self.preview_font.configure(size=size) def validate_font_size(self, d, ch, V): """Validation of the size entry content.""" l = [i for i in self.sizes if i[:len(ch)] == ch] i = None if l: i = self.sizes.index(l[0]) elif ch.isdigit(): sizes = list(self.sizes) sizes.append(ch) sizes.sort(key=lambda x: int(x)) i = min(sizes.index(ch), len(self.sizes)) if i is not None: self.list_size.selection_clear(0, "end") self.list_size.selection_set(i) deb = self.list_size.nearest(0) fin = self.list_size.nearest(self.list_size.winfo_height()) if V != "forced": if i < deb or i > fin: self.list_size.see(i) return True if d == '1': return ch.isdigit() else: return True def tab(self, event): """Move at the end of selected text on tab press.""" self.entry_family = event.widget self.entry_family.selection_clear() self.entry_family.icursor("end") return "break" def validate_font_family(self, action, modif, pos, prev_txt, V): """Completion of the text in the entry with existing font names.""" if self.entry_family.selection_present(): sel = self.entry_family.selection_get() txt = prev_txt.replace(sel, '') else: txt = prev_txt if action == "0": txt = txt[:int(pos)] + txt[int(pos) + 1:] return True else: txt = txt[:int(pos)] + modif + txt[int(pos):] ch = txt.replace(" ", "\ ") l = [i for i in self.fonts if i[:len(ch)] == ch] if l: i = self.fonts.index(l[0]) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) deb = self.list_family.nearest(0) fin = self.list_family.nearest(self.list_family.winfo_height()) index = self.entry_family.index("insert") self.entry_family.delete(0, "end") self.entry_family.insert(0, l[0].replace("\ ", " ")) self.entry_family.selection_range(index + 1, "end") self.entry_family.icursor(index + 1) if V != "forced": if i < deb or i > fin: self.list_family.see(i) return True else: return False def update_entry_family(self, event=None): """Update family entry when an item is selected in the family listbox.""" # family = self.list_family.get("@%i,%i" % (event.x , event.y)) family = self.list_family.get(self.list_family.curselection()[0]) self.entry_family.delete(0, "end") self.entry_family.insert(0, family) self.entry_family.selection_clear() self.entry_family.icursor("end") self.change_font_family() def update_entry_size(self, event): """Update size entry when an item is selected in the size listbox.""" # size = self.list_size.get("@%i,%i" % (event.x , event.y)) size = self.list_size.get(self.list_size.curselection()[0]) self.var_size.set(size) self.change_font_size() def ok(self): """Validate choice.""" self.res = self.preview_font.actual() self.quit() def get_res(self): """Return chosen font.""" return self.res def quit(self): self.destroy()
class myListBox(Frame): def __init__(self, root, items=[], id='', item_select_handler=default_item_select_handler, smode=tki.EXTENDED): self.item_count = 0 self.root = root self.item_select_handler = item_select_handler Frame.__init__(self, self.root) self.id = id vscrollbar = Scrollbar(self, orient=tki.VERTICAL) vscrollbar.pack(side=tki.RIGHT, fill=tki.Y) hscrollbar = Scrollbar(self, orient=tki.HORIZONTAL) hscrollbar.pack(side=tki.BOTTOM, fill=tki.X) ## mode can be: SINGLE, BROWSE, MULTIPLE, EXTENDED ## selectmode ## ## Determines how many items can be selected, and how mouse drags affect the selection − ## ## BROWSE − Normally, you can only select one line out of a listbox. If you click on an item and then drag to a different line, the selection will follow the mouse. This is the default. ## SINGLE − You can only select one line, and you can't drag the mouse.wherever you click button 1, that line is selected. ## MULTIPLE − You can select any number of lines at once. Clicking on any line toggles whether or not it is selected. ## EXTENDED − You can select any adjacent group of lines at once by clicking on the first line and dragging to the last line. self.list = Listbox(self, selectmode=smode, exportselection=0, xscrollcommand=hscrollbar.set, yscrollcommand=vscrollbar.set) for i in items: assert (type(i) is str) self.list.insert(items.index(i), i) self.list.pack(fill=tki.BOTH, expand=1) self.list.bind('<Double-Button-1>', self.item_select_handler) self.list.bind('<1>', self.item_select_handler) self.list.bind('<Return>', self.item_select_handler) ## DO NOT catch ListboxSelect event, because: ## a) it is not associated with (x_root, y_root) and (x,y) coordinates, so the popup appears always at (0,0) of the main root window ## b) it duplicates the click event catching self.list.bind( '<1>', self.item_select_handler ) and generates a second event ## self.list.bind( '<<ListboxSelect>>', self.item_select_handler ) hscrollbar.config(command=self.list.xview) vscrollbar.config(command=self.list.yview) self.pack(side=tki.LEFT, fill=tki.BOTH, expand=1) self.current = self.list.curselection() def insert_item(self, pos, item): self.list.insert(pos, item) self.item_count += 1 self.activate(pos) self.index(pos) def delete(self, start, end=None): assert (type(start) is int) if (end is None): self.list.delete(start) self.item_count -= 1 else: assert ((type(end) is int) or (end == tki.END)) if (type(end) is str): self.list.delete(start, (self.item_count - 1)) self.item_count -= (self.item_count - start) else: self.list.delete(start, end) self.item_count -= (end - start) + 1 def select_set(self, i): self.list.selection_clear(0, tki.END) self.list.select_set(i) def activate(self, i): self.list.activate(i) def index(self, i): self.list.index(i) def generate_select_event(self, pos=None): assert (pos is not None) if (pos is not None): self.activate(pos) self.index(pos) self.select_set(pos) self.list.event_generate("<<ListboxSelect>>") ## def poll(self): ## now = self.list.curselection() ## if now != self.current: ## self.list_has_changed( now ) ## self.current = now ## self.after( 250, self.poll ) def list_has_changed(self, selection): print('widget {0} selection is {1}'.format(self.id, selection))
class FilePane: def __init__(self, root, items=list(), on_selection=list(), on_close=list()): self.items = items self.item_count = len(items) self.on_selection = on_selection self.on_close = on_close self.window = Toplevel() self.window.title('Files') self.window.transient(root) self.window.protocol("WM_DELETE_WINDOW", self.on_window_close) self.menubar = Menu(self.window) self.menubar.add_command(label='Previous', command=self.previous) self.menubar.add_command(label='Next', command=self.next) # Display the menu self.window.config(menu=self.menubar) self.scrollbar = Scrollbar(self.window, orient='vertical') self.listbox = Listbox(self.window, yscrollcommand=self.scrollbar.set, exportselection=False) self.set_items(items) self.scrollbar.config(command=self.listbox.yview) self.scrollbar.pack(side='right', fill='y') self.listbox.pack(side='left', fill='both', expand=1) self.listbox.bind('<<ListboxSelect>>', self.on_select) def on_window_close(self): for callback in self.on_close: callback() self.window.withdraw() def hide(self): self.window.withdraw() def show(self): self.window.deiconify() def selected_item(self): return self.items[self.listbox.curselection()[0]] def select_index(self, index): self.listbox.selection_clear(0, END) # From https://stackoverflow.com/a/25451279/11628429 self.listbox.select_set( index) #This only sets focus on the first item. self.listbox.event_generate("<<ListboxSelect>>") def set_items(self, items): self.items = items self.item_count = len(items) self.listbox.delete(0, END) for item in items: item = path.split(item)[1] self.listbox.insert(END, item) self.select_index(0) def previous(self): index = self.listbox.curselection()[0] - 1 if index < 0: index = self.item_count - 1 self.select_index(index) def next(self): index = self.listbox.curselection()[0] + 1 if index >= self.item_count: index = 0 self.select_index(index) def on_select(self, event): if self.on_selection: for callback in self.on_selection: callback(self.selected_item())
class FontChooser(Toplevel): """ Font chooser toplevel """ def __init__(self, master, font_dict={}, text="Abcd", title="Font Chooser", **kwargs): """ Create a new FontChooser instance. font: dictionnary, like the one returned by the .actual method of a Font object {'family': 'DejaVu Sans', 'overstrike':False, 'size': 12, 'slant': 'italic' or 'roman', 'underline': False, 'weight': 'bold' or 'normal'} text: text to be displayed in the preview label title: window title **kwargs: additional keyword arguments to be passed to Toplevel.__init__ """ Toplevel.__init__(self, master, **kwargs) self.title(title) self.resizable(False, False) self.protocol("WM_DELETE_WINDOW", self.quit) self._validate_family = self.register(self.validate_font_family) self._validate_size = self.register(self.validate_font_size) # variable storing the chosen font self.res = "" style = Style(self) style.configure("prev.TLabel", background="white") bg = style.lookup("TLabel", "background") self.configure(bg=bg) # family list self.fonts = list(set(families())) self.fonts.append("TkDefaultFont") self.fonts.sort() for i in range(len(self.fonts)): self.fonts[i] = self.fonts[i].replace(" ", "\ ") max_length = int(2.5 * max([len(font) for font in self.fonts])) // 3 self.sizes = [ "%i" % i for i in (list(range(6, 17)) + list(range(18, 32, 2))) ] # font default font_dict["weight"] = font_dict.get("weight", "normal") font_dict["slant"] = font_dict.get("slant", "roman") font_dict["family"] = font_dict.get("family", self.fonts[0].replace('\ ', ' ')) font_dict["size"] = font_dict.get("size", 10) # Widgets creation options_frame = Frame(self, relief='groove', borderwidth=2) self.font_family = StringVar(self, " ".join(self.fonts)) self.font_size = StringVar(self, " ".join(self.sizes)) self.var_bold = BooleanVar(self, font_dict["weight"] == "bold") b_bold = Checkbutton(options_frame, text=TR["Bold"], command=self.toggle_bold, variable=self.var_bold) b_bold.grid(row=0, sticky="w", padx=4, pady=(4, 2)) self.var_italic = BooleanVar(self, font_dict["slant"] == "italic") b_italic = Checkbutton(options_frame, text=TR["Italic"], command=self.toggle_italic, variable=self.var_italic) b_italic.grid(row=1, sticky="w", padx=4, pady=2) self.var_size = StringVar(self) self.entry_family = Entry(self, width=max_length, validate="key", validatecommand=(self._validate_family, "%d", "%S", "%i", "%s", "%V")) entry_size = Entry(self, width=4, validate="key", textvariable=self.var_size, validatecommand=(self._validate_size, "%d", "%P", "%V")) self.list_family = Listbox(self, selectmode="browse", listvariable=self.font_family, highlightthickness=0, exportselection=False, width=max_length) self.list_size = Listbox(self, selectmode="browse", listvariable=self.font_size, highlightthickness=0, exportselection=False, width=4) scroll_family = Scrollbar(self, orient='vertical', command=self.list_family.yview) scroll_size = Scrollbar(self, orient='vertical', command=self.list_size.yview) self.preview_font = Font(self, **font_dict) if len(text) > 30: text = text[:30] self.preview = Label(self, relief="groove", style="prev.TLabel", text=text, font=self.preview_font, anchor="center") # Widget configuration self.list_family.configure(yscrollcommand=scroll_family.set) self.list_size.configure(yscrollcommand=scroll_size.set) self.entry_family.insert(0, font_dict["family"]) self.entry_family.selection_clear() self.entry_family.icursor("end") entry_size.insert(0, font_dict["size"]) i = self.fonts.index(self.entry_family.get().replace(" ", "\ ")) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) self.list_family.see(i) i = self.sizes.index(entry_size.get()) self.list_size.selection_clear(0, "end") self.list_size.selection_set(i) self.list_size.see(i) self.entry_family.grid(row=0, column=0, sticky="ew", pady=(10, 1), padx=(10, 0)) entry_size.grid(row=0, column=2, sticky="ew", pady=(10, 1), padx=(10, 0)) self.list_family.grid(row=1, column=0, sticky="nsew", pady=(1, 10), padx=(10, 0)) self.list_size.grid(row=1, column=2, sticky="nsew", pady=(1, 10), padx=(10, 0)) scroll_family.grid(row=1, column=1, sticky='ns', pady=(1, 10)) scroll_size.grid(row=1, column=3, sticky='ns', pady=(1, 10)) options_frame.grid(row=0, column=4, rowspan=2, padx=10, pady=10, ipadx=10) self.preview.grid(row=2, column=0, columnspan=5, sticky="eswn", padx=10, pady=(0, 10), ipadx=4, ipady=4) button_frame = Frame(self) button_frame.grid(row=3, column=0, columnspan=5, pady=(0, 10), padx=10) Button(button_frame, text="Ok", command=self.ok).grid(row=0, column=0, padx=4, sticky='ew') Button(button_frame, text=TR["Cancel"], command=self.quit).grid(row=0, column=1, padx=4, sticky='ew') self.list_family.bind('<<ListboxSelect>>', self.update_entry_family) self.list_size.bind('<<ListboxSelect>>', self.update_entry_size, add=True) self.list_family.bind("<KeyPress>", self.keypress) self.entry_family.bind("<Return>", self.change_font_family) self.entry_family.bind("<Tab>", self.tab) entry_size.bind("<Return>", self.change_font_size) self.entry_family.bind("<Down>", self.down_family) entry_size.bind("<Down>", self.down_size) self.entry_family.bind("<Up>", self.up_family) entry_size.bind("<Up>", self.up_size) # bind Ctrl+A to select all instead of go to beginning self.bind_class("TEntry", "<Control-a>", self.select_all) self.update_idletasks() self.grab_set() self.entry_family.focus_set() self.lift() def select_all(self, event): event.widget.selection_range(0, "end") def keypress(self, event): key = event.char.lower() l = [i for i in self.fonts if i[0].lower() == key] if l: i = self.fonts.index(l[0]) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) self.list_family.see(i) self.update_entry_family() def up_family(self, event): try: txt = self.entry_family.get().replace(" ", "\ ") l = [i for i in self.fonts if i[:len(txt)] == txt] if l: self.list_family.selection_clear(0, "end") i = self.fonts.index(l[0]) if i > 0: self.list_family.selection_set(i - 1) self.list_family.see(i - 1) else: i = len(self.fonts) self.list_family.see(i - 1) self.list_family.select_set(i - 1) except TclError: i = len(self.fonts) self.list_family.see(i - 1) self.list_family.select_set(i - 1) self.list_family.event_generate('<<ListboxSelect>>') def up_size(self, event): try: s = self.var_size.get() i = self.sizes.index(s) self.list_size.selection_clear(0, "end") if i > 0: self.list_size.selection_set(i - 1) self.list_size.see(i - 1) else: i = len(self.sizes) self.list_size.see(i - 1) self.list_size.select_set(i - 1) except TclError: i = len(self.sizes) self.list_size.see(i - 1) self.list_size.select_set(i - 1) self.list_size.event_generate('<<ListboxSelect>>') def down_family(self, event): try: txt = self.entry_family.get().replace(" ", "\ ") l = [i for i in self.fonts if i[:len(txt)] == txt] if l: self.list_family.selection_clear(0, "end") i = self.fonts.index(l[0]) if i < len(self.fonts) - 1: self.list_family.selection_set(i + 1) self.list_family.see(i + 1) else: self.list_family.see(0) self.list_family.select_set(0) except TclError: self.list_family.selection_set(0) self.list_family.event_generate('<<ListboxSelect>>') def down_size(self, event): try: s = self.var_size.get() i = self.sizes.index(s) self.list_size.selection_clear(0, "end") if i < len(self.sizes) - 1: self.list_size.selection_set(i + 1) self.list_size.see(i + 1) else: self.list_size.see(0) self.list_size.select_set(0) except TclError: self.list_size.selection_set(0) self.list_size.event_generate('<<ListboxSelect>>') def toggle_bold(self): b = self.var_bold.get() self.preview_font.configure(weight=["normal", "bold"][b]) def toggle_italic(self): b = self.var_italic.get() self.preview_font.configure(slant=["roman", "italic"][b]) def toggle_underline(self): b = self.var_underline.get() self.preview_font.configure(underline=b) def toggle_overstrike(self): b = self.var_overstrike.get() self.preview_font.configure(overstrike=b) def change_font_family(self, event=None): family = self.entry_family.get() if family.replace(" ", "\ ") in self.fonts: self.preview_font.configure(family=family) def change_font_size(self, event=None): size = int(self.var_size.get()) self.preview_font.configure(size=size) def validate_font_size(self, d, ch, V): ''' Validation of the size entry content ''' l = [i for i in self.sizes if i[:len(ch)] == ch] if l: i = self.sizes.index(l[0]) self.list_size.selection_clear(0, "end") self.list_size.selection_set(i) deb = self.list_size.nearest(0) fin = self.list_size.nearest(self.list_size.winfo_height()) if V != "forced": if i < deb or i > fin: self.list_size.see(i) return True if d == '1': return ch.isdigit() else: return True def tab(self, event): self.entry_family = event.widget self.entry_family.selection_clear() self.entry_family.icursor("end") return "break" def validate_font_family(self, action, modif, pos, prev_txt, V): """ completion of the text in the path entry with existing folder/file names """ if self.entry_family.selection_present(): sel = self.entry_family.selection_get() txt = prev_txt.replace(sel, '') else: txt = prev_txt if action == "0": txt = txt[:int(pos)] + txt[int(pos) + 1:] return True else: txt = txt[:int(pos)] + modif + txt[int(pos):] ch = txt.replace(" ", "\ ") l = [i for i in self.fonts if i[:len(ch)] == ch] if l: i = self.fonts.index(l[0]) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) deb = self.list_family.nearest(0) fin = self.list_family.nearest(self.list_family.winfo_height()) index = self.entry_family.index("insert") self.entry_family.delete(0, "end") self.entry_family.insert(0, l[0].replace("\ ", " ")) self.entry_family.selection_range(index + 1, "end") self.entry_family.icursor(index + 1) if V != "forced": if i < deb or i > fin: self.list_family.see(i) return True else: return False def update_entry_family(self, event=None): # family = self.list_family.get("@%i,%i" % (event.x , event.y)) family = self.list_family.get(self.list_family.curselection()[0]) self.entry_family.delete(0, "end") self.entry_family.insert(0, family) self.entry_family.selection_clear() self.entry_family.icursor("end") self.change_font_family() def update_entry_size(self, event): # size = self.list_size.get("@%i,%i" % (event.x , event.y)) size = self.list_size.get(self.list_size.curselection()[0]) self.var_size.set(size) self.change_font_size() def ok(self): self.res = self.preview_font.actual() self.quit() def get_res(self): return self.res def quit(self): self.destroy()