def onRightClick(self, event): if self.slot.status == 'Full ': popup = Menu(self,tearoff=0) popup.add_command(label= u'load', command=partial(self.menu_action, self.slot.slot)) try: popup.tk_popup(event.x_root, event.y_root, 0) finally: popup.grab_release() self.updated()
def onRightClick(self, event): if self.slot.status != 'Empty': popup = Menu(self,tearoff=0) unloadmenu = Menu(self, tearoff = 0) for slot in self.slot.device.storage_slots: if slot.status == 'Empty': unloadmenu.add_command(label= u'storage slot %s' % slot.slot, command=partial(self.menu_action, slot.slot)) popup.add_cascade(label='unload',menu =unloadmenu) try: popup.tk_popup(event.x_root, event.y_root, 0) finally: popup.grab_release() self.updated()
def _launch_menu(self, coords): """ Callback function in response to the pressing of the Return key Launches a context menu based on the location of the mouse :param coords: :return: """ # Configure the "static" menu entries -- they can't be static without seriously destroying readability # due to the Python version that is being used -.- so now it has to be not optimal until I find a better # solution p_menu = Menu(self._canvas) item = self._get_current_item((self._cache["x"], self._cache["y"])) updated_coords = self._canvas_to_screen((self._cache["x"], self._cache["y"])) if item is None: # No node is currently selected, create the general menu p_menu.add_command(label="Place Room", command=lambda: self.create_new_node((self._cache["x"], self._cache["y"]))) p_menu.add_command(label="Delete All", command=lambda: self.delete_all()) p_menu.tk_popup(updated_coords[0], updated_coords[1]) return if self._is_node(item): # Create the node specific menu p_menu.add_command(label="Place Object", command=lambda: self._mark_object((self._cache["x"], self._cache["y"]))) p_menu.add_command(label="Edit Room", command=lambda: self._selection_operation((self._cache["x"], self._cache["y"]))) p_menu.add_command(label="Delete Room", command=lambda: self.delete_node(self._get_current_item((self._cache["x"], self._cache["y"])))) p_menu.add_command(label="Mark as start", command=lambda: self._mark_start_node(self._get_current_item((self._cache["x"], self._cache["y"])))) if self._is_object(item): # Launch the node menu as well as an an added option for selecting stuff to edit an object p_menu.add_command(label="Edit Object", command=lambda: self._edit_object(coords)) p_menu.add_command(label="Delete Object", command=lambda: self._delete_object(self._get_current_item((self._cache["x"], self._cache["y"])))) p_menu.delete(0) p_menu.tk_popup(updated_coords[0], updated_coords[1]) return if self._is_edge(item): p_menu.add_command(label="Edit Corridor", command=lambda: self._selection_operation((self._cache["x"], self._cache["y"]))) p_menu.add_command(label="Delete Corridor", command=lambda: self.delete_edge(self._get_current_item((self._cache["x"], self._cache["y"])))) p_menu.tk_popup(updated_coords[0], updated_coords[1]) return self._clear_cache(coords)
class LintGui(object): """Build and control a window to interact with pylint""" def __init__(self, root=None): """init""" self.root = root or Tk() self.root.title('Pylint') #reporter self.reporter = None #message queue for output from reporter self.msg_queue = Queue.Queue() self.msgs = [] self.visible_msgs = [] self.filenames = [] self.rating = StringVar() self.tabs = {} self.report_stream = BasicStream(self) self.differ = differ.Differ() #gui objects self.lbMessages = None self.showhistory = None self.results = None self.btnRun = None self.information_box = None self.convention_box = None self.refactor_box = None self.warning_box = None self.error_box = None self.fatal_box = None self.txtModule = None self.status = None self.msg_type_dict = None self.init_gui() def init_gui(self): """init helper""" #setting up frames top_frame = Frame(self.root) mid_frame = Frame(self.root) radio_frame = Frame(self.root) res_frame = Frame(self.root) msg_frame = Frame(self.root) check_frame = Frame(self.root) history_frame = Frame(self.root) btn_frame = Frame(self.root) rating_frame = Frame(self.root) top_frame.pack(side=TOP, fill=X) mid_frame.pack(side=TOP, fill=X) history_frame.pack(side=TOP, fill=BOTH, expand=True) radio_frame.pack(side=TOP, fill=BOTH, expand=True) rating_frame.pack(side=TOP, fill=BOTH, expand=True) res_frame.pack(side=TOP, fill=BOTH, expand=True) check_frame.pack(side=TOP, fill=BOTH, expand=True) msg_frame.pack(side=TOP, fill=BOTH, expand=True) btn_frame.pack(side=TOP, fill=X) # Binding F5 application-wide to run lint self.root.bind('<F5>', self.run_lint) #Message ListBox rightscrollbar = Scrollbar(msg_frame) rightscrollbar.pack(side=RIGHT, fill=Y) bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL) bottomscrollbar.pack(side=BOTTOM, fill=X) self.lbMessages = Listbox(msg_frame, yscrollcommand=rightscrollbar.set, xscrollcommand=bottomscrollbar.set, bg="white") self.lbMessages.bind("<Double-Button-1>", self.show_sourcefile) self.lbMessages.pack(expand=True, fill=BOTH) rightscrollbar.config(command=self.lbMessages.yview) bottomscrollbar.config(command=self.lbMessages.xview) #Message context menu self.mnMessages = Menu(self.lbMessages, tearoff=0) self.mnMessages.add_command(label="View in sourcefile", command=self.show_sourcefile) self.mnMessages.add_command(label="Add to ignore patchfile", command=self.add_to_ignore_patchfile) self.lbMessages.bind("<Button-3>", self.show_messages_context) #History ListBoxes rightscrollbar2 = Scrollbar(history_frame) rightscrollbar2.pack(side=RIGHT, fill=Y) bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL) bottomscrollbar2.pack(side=BOTTOM, fill=X) self.showhistory = Listbox(history_frame, yscrollcommand=rightscrollbar2.set, xscrollcommand=bottomscrollbar2.set, bg="white") self.showhistory.pack(expand=True, fill=BOTH) rightscrollbar2.config(command=self.showhistory.yview) bottomscrollbar2.config(command=self.showhistory.xview) self.showhistory.bind('<Double-Button-1>', self.select_recent_file) self.set_history_window() #status bar self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W) self.status.pack(side=BOTTOM, fill=X) #labels self.lblRatingLabel = Label(rating_frame, text='Rating:') self.lblRatingLabel.pack(side=LEFT) self.lblRating = Label(rating_frame, textvariable=self.rating) self.lblRating.pack(side=LEFT) Label(mid_frame, text='Recently Used:').pack(side=LEFT) Label(top_frame, text='Module or package').pack(side=LEFT) #file textbox self.txtModule = Entry(top_frame, background='white') self.txtModule.bind('<Return>', self.run_lint) self.txtModule.pack(side=LEFT, expand=True, fill=X) #results box rightscrollbar = Scrollbar(res_frame) rightscrollbar.pack(side=RIGHT, fill=Y) bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL) bottomscrollbar.pack(side=BOTTOM, fill=X) self.results = Listbox(res_frame, yscrollcommand=rightscrollbar.set, xscrollcommand=bottomscrollbar.set, bg="white", font="Courier") self.results.pack(expand=True, fill=BOTH, side=BOTTOM) rightscrollbar.config(command=self.results.yview) bottomscrollbar.config(command=self.results.xview) #buttons Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT) Button(top_frame, text='Open Package', command=(lambda: self.file_open(package=True))).pack(side=LEFT) self.btnRun = Button(top_frame, text='Run', command=self.run_lint) self.btnRun.pack(side=LEFT) Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM) #radio buttons self.information_box = IntVar() self.convention_box = IntVar() self.refactor_box = IntVar() self.warning_box = IntVar() self.error_box = IntVar() self.fatal_box = IntVar() i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'], variable=self.information_box, command=self.refresh_msg_window) c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'], variable=self.convention_box, command=self.refresh_msg_window) r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'], variable=self.refactor_box, command=self.refresh_msg_window) w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'], variable=self.warning_box, command=self.refresh_msg_window) e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'], variable=self.error_box, command=self.refresh_msg_window) f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'], variable=self.fatal_box, command=self.refresh_msg_window) i.select() c.select() r.select() w.select() e.select() f.select() i.pack(side=LEFT) c.pack(side=LEFT) r.pack(side=LEFT) w.pack(side=LEFT) e.pack(side=LEFT) f.pack(side=LEFT) #check boxes self.box = StringVar() # XXX should be generated report = Radiobutton(radio_frame, text="Report", variable=self.box, value="Report", command=self.refresh_results_window) rawMet = Radiobutton(radio_frame, text="Raw metrics", variable=self.box, value="Raw metrics", command=self.refresh_results_window) dup = Radiobutton(radio_frame, text="Duplication", variable=self.box, value="Duplication", command=self.refresh_results_window) ext = Radiobutton(radio_frame, text="External dependencies", variable=self.box, value="External dependencies", command=self.refresh_results_window) stat = Radiobutton(radio_frame, text="Statistics by type", variable=self.box, value="Statistics by type", command=self.refresh_results_window) msgCat = Radiobutton(radio_frame, text="Messages by category", variable=self.box, value="Messages by category", command=self.refresh_results_window) msg = Radiobutton(radio_frame, text="Messages", variable=self.box, value="Messages", command=self.refresh_results_window) sourceFile = Radiobutton(radio_frame, text="Source File", variable=self.box, value="Source File", command=self.refresh_results_window) report.select() report.grid(column=0, row=0, sticky=W) rawMet.grid(column=1, row=0, sticky=W) dup.grid(column=2, row=0, sticky=W) msg.grid(column=3, row=0, sticky=W) stat.grid(column=0, row=1, sticky=W) msgCat.grid(column=1, row=1, sticky=W) ext.grid(column=2, row=1, sticky=W) sourceFile.grid(column=3, row=1, sticky=W) #dictionary for check boxes and associated error term self.msg_type_dict = { 'I': lambda: self.information_box.get() == 1, 'C': lambda: self.convention_box.get() == 1, 'R': lambda: self.refactor_box.get() == 1, 'E': lambda: self.error_box.get() == 1, 'W': lambda: self.warning_box.get() == 1, 'F': lambda: self.fatal_box.get() == 1 } self.txtModule.focus_set() def select_recent_file(self, event): """adds the selected file in the history listbox to the Module box""" if not self.showhistory.size(): return selected = self.showhistory.curselection() item = self.showhistory.get(selected) #update module self.txtModule.delete(0, END) self.txtModule.insert(0, item) def refresh_msg_window(self): """refresh the message window with current output""" #clear the window self.lbMessages.delete(0, END) self.visible_msgs = [] for msg in self.msgs: if (self.msg_type_dict.get(msg.C)()): self.visible_msgs.append(msg) msg_str = convert_to_string(msg) self.lbMessages.insert(END, msg_str) fg_color = COLORS.get(msg_str[:3], 'black') self.lbMessages.itemconfigure(END, fg=fg_color) def refresh_results_window(self): """refresh the results window with current output""" #clear the window self.results.delete(0, END) try: for res in self.tabs[self.box.get()]: self.results.insert(END, res) except: pass def process_incoming(self): """process the incoming messages from running pylint""" while self.msg_queue.qsize(): try: msg = self.msg_queue.get(0) if msg == "DONE": self.report_stream.output_contents() return False #adding message to list of msgs self.msgs.append(msg) #displaying msg if message type is selected in check box if (self.msg_type_dict.get(msg.C)()): self.visible_msgs.append(msg) msg_str = convert_to_string(msg) self.lbMessages.insert(END, msg_str) fg_color = COLORS.get(msg_str[:3], 'black') self.lbMessages.itemconfigure(END, fg=fg_color) except Queue.Empty: pass return True def periodic_call(self): """determine when to unlock the run button""" if self.process_incoming(): self.root.after(100, self.periodic_call) else: #enabling button so it can be run again self.btnRun.config(state=NORMAL) def mainloop(self): """launch the mainloop of the application""" self.root.mainloop() def quit(self, _=None): """quit the application""" self.root.quit() def halt(self): """program halt placeholder""" return def file_open(self, package=False, _=None): """launch a file browser""" if not package: filename = askopenfilename(parent=self.root, filetypes=[('pythonfiles', '*.py'), ('allfiles', '*')], title='Select Module') else: filename = askdirectory(title="Select A Folder", mustexist=1) if filename == (): return self.txtModule.delete(0, END) self.txtModule.insert(0, filename) def update_filenames(self): """update the list of recent filenames""" filename = self.txtModule.get() if not filename: filename = os.getcwd() if filename + '\n' in self.filenames: index = self.filenames.index(filename + '\n') self.filenames.pop(index) #ensure only 10 most recent are stored if len(self.filenames) == 10: self.filenames.pop() self.filenames.insert(0, filename + '\n') def set_history_window(self): """update the history window with info from the history file""" #clear the window self.showhistory.delete(0, END) # keep the last 10 most recent files try: view_history = open(HOME + HISTORY, 'r') for hist in view_history.readlines(): if not hist in self.filenames: self.filenames.append(hist) self.showhistory.insert(END, hist.split('\n')[0]) view_history.close() except IOError: # do nothing since history file will be created later return def run_lint(self, _=None): """launches pylint""" self.update_filenames() self.root.configure(cursor='watch') self.reporter = GUIReporter(self, output=self.report_stream) module = self.txtModule.get() if not module: module = os.getcwd() #cleaning up msgs and windows self.msgs = [] self.visible_msgs = [] self.lbMessages.delete(0, END) self.tabs = {} self.results.delete(0, END) self.btnRun.config(state=DISABLED) #setting up a worker thread to run pylint worker = Thread(target=lint_thread, args=( module, self.reporter, self, )) self.periodic_call() worker.start() # Overwrite the .pylint-gui-history file with all the new recently added files # in order from filenames but only save last 10 files write_history = open(HOME + HISTORY, 'w') write_history.writelines(self.filenames) write_history.close() self.set_history_window() self.root.configure(cursor='') def show_sourcefile(self, event=None): selected = self.lbMessages.curselection() if not selected: return msg = self.visible_msgs[int(selected[0])] scroll = msg.line - 3 if scroll < 0: scroll = 0 self.tabs["Source File"] = open(msg.abspath, "r").readlines() self.box.set("Source File") self.refresh_results_window() self.results.yview(scroll) self.results.select_set(msg.line - 1) def show_messages_context(self, event): """Show the message listbox's context menu""" # Select the item that was clicked index = self.lbMessages.nearest(event.y) self.lbMessages.selection_clear(0, END) self.lbMessages.selection_set(index) self.lbMessages.activate(index) self.mnMessages.tk_popup(event.x_root, event.y_root) def add_to_ignore_patchfile(self, event=None): """ Add the selected message to the ignore patchfile. This means that this message will now be ignored by pylint-patcher. """ selected = self.lbMessages.curselection() if not selected: return selected_index = int(selected[0]) msg = self.visible_msgs[selected_index] self.differ.add_disable_pragma(msg.abspath, msg.line, msg.symbol) self.differ.diff() del self.msgs[self.msgs.index(msg)] del self.visible_msgs[selected_index] self.lbMessages.delete(selected_index)
class SuperText(Text): def __init__(self, parent, scrollbar=True, **kw): self.parent = parent frame = Frame(parent) frame.pack(fill='both', expand=True) # text widget Text.__init__(self, frame, **kw) self.pack(side='left', fill='both', expand=True) # scrollbar if scrollbar: scrb = Scrollbar(frame, orient='vertical', command=self.yview) self.config(yscrollcommand=scrb.set) scrb.pack(side='right', fill='y') # pop-up menu self.popup = Menu(self, tearoff=0) self.popup.add_command(label='Cut', command=self._cut) self.popup.add_command(label='Copy', command=self._copy) self.popup.add_command(label='Paste', command=self._paste) self.popup.add_separator() self.popup.add_command(label='Select All', command=self._select_all) self.popup.add_command(label='Clear All', command=self._clear_all) self.bind('<Button-3>', self._show_popup) def apply_theme(self, theme='standard'): '''theme=['standard', 'typewriter', 'terminal']''' if theme == 'typewriter': '''takes all inserted text and inserts it one char every 100ms''' text = self.get('1.0', 'end') self.delete('1.0', 'end') self.char_index = 0 self._typewriter([char for char in text]) elif theme == 'terminal': '''blocky insert cursor''' self.cursor = '1.0' self.fg = self.cget('fg') self.bg = self.cget('bg') self.switch = self.fg self.config(insertwidth=0) self._blink_cursor() self._place_cursor() def _show_popup(self, event): '''right-click popup menu''' if self.parent.focus_get() != self: self.focus_set() try: self.popup.tk_popup(event.x_root, event.y_root, 0) finally: self.popup.grab_release() def _cut(self): try: selection = self.get(*self.tag_ranges('sel')) self.clipboard_clear() self.clipboard_append(selection) self.delete(*self.tag_ranges('sel')) except TypeError: pass def _copy(self): try: selection = self.get(*self.tag_ranges('sel')) self.clipboard_clear() self.clipboard_append(selection) except TypeError: pass def _paste(self): self.insert('insert', self.selection_get(selection='CLIPBOARD')) def _select_all(self): '''selects all text''' self.tag_add('sel', '1.0', 'end-1c') def _clear_all(self): '''erases all text''' isok = askokcancel('Clear All', 'Erase all text?', parent=self, default='ok') if isok: self.delete('1.0', 'end') def _typewriter(self, text): # theme: typewriter '''after the theme is applied, this method takes all the inserted text and types it out one character every 100ms''' self.insert('insert', text[self.char_index]) self.char_index += 1 if self.char_index == len(text): self.after_cancel(self.typer) else: self.typer = self.after(100, self._typewriter, text) def _place_cursor(self): # theme: terminal '''check the position of the cursor against the last known position every 15ms and update the cursorblock tag as needed''' current_index = self.index('insert') if self.cursor != current_index: self.cursor = current_index self.tag_delete('cursorblock') start = self.index('insert') end = self.index('insert+1c') if start[0] != end[0]: self.insert(start, ' ') end = self.index('insert') self.tag_add('cursorblock', start, end) self.mark_set('insert', self.cursor) self.after(15, self._place_cursor) def _blink_cursor(self): # theme: terminal '''alternate the background color of the cursorblock tagged text every 600 milliseconds''' if self.switch == self.fg: self.switch = self.bg else: self.switch = self.fg self.tag_config('cursorblock', background=self.switch) self.after(600, self._blink_cursor)
class VisualVolumes(MyFrame): #Paths need to be changed when used in a different environment #Also segmentation accepts only one file now! def __init__(self, image_paths=None, segm_path=None, supervoxel_id_path=None, topframe=None): MyFrame.__init__(self, topframe=None) Style().theme_use('clam') self.set_title('Visualise Volumes') self.set_variables(image_paths, segm_path=segm_path, supervoxel_id_path=supervoxel_id_path) self.create_ui(image_paths=image_paths, supervoxel_id_path=supervoxel_id_path) ########## variable setting ###########################F def set_variables(self, image_paths, segm_path=None, supervoxel_id_path=None): """Set initial alpha for opacity, boundaries, undo-redo stacks, and paths for modalities, segmentation and supervoxels""" #Alpha value for opacity initialization self.alpha = 0.2 self._axis = 0 self.show_supervoxels = True self._set_images(segm_path, image_paths, supervoxel_id_path) # set boundaries for all images: self.boundaries = ms.get_boundaries_series(image_paths) # set stacks for CTRL+Z and CTRL+Y # CTRL+Z to undo # CTRL+Y to redo self.undo_stack = [] self.redo_stack = [] self.image_nb = len(image_paths) self.selected_id_list = [] #Keybindings for undo and redo actions self.bind('<Control-z>', self.undo_action) self.bind('<Control-y>', self.redo_action) self.bind('<t>', self.toggle_segm) self.bind("<h>", self.set_healthy) self.bind("<r>", self.unselect_all) self.bind("<c>", self.print_com) self.bind("<i>", self.inverse_selection) for j in range(len(label_hot_keys)): self.bind("<%s>" % label_hot_keys[j], lambda event, arg0=j : self.change_label(arg0)) self.bind("<Right>", self.slice_up) self.bind("<Left>", self.slice_down) self.focus_set() def _set_images(self, segm_path, image_paths, supervoxel_id_path): """ Set and check image and segmentation arrays and dimensions. """ # Shapes for all modalities shapes = [oitk.get_itk_array(path).shape \ for path in image_paths] if len(set(shapes)) != 1: err = 'Images are not of same dimension' raise ValueError(err) self.dim = shapes[0] # set the images and the image paths self.images = [oitk.get_itk_array(path) \ for path in image_paths] self.images_original = np.copy(self.images) # Get segmentation from segmentation path if given self.segm = None self.segm_path = None if segm_path is not None: self.segm = oitk.get_itk_array(segm_path) self.segm_path = segm_path # Get supervoxels from supervoxel path if given self.supervoxel_border = None self.supervoxel_id = None if supervoxel_id_path is not None: # get supervoxels and adapt if segmentation is at hand self.supervoxel_id = oitk.get_itk_array(supervoxel_id_path) if self.segm is not None: self.supervoxel_id = utils.adapt_borders(self.segm, self.supervoxel_id) # get borders of the supervoxels self.supervoxel_border = utils.get_borders(self.supervoxel_id) ########## Frame setting ############ def create_ui(self, image_paths, supervoxel_id_path): """This function creates the UI elements.""" sub_frame = Frame(self) sub_frame.grid(column=1, row=0, columnspan=self.image_nb) # create a sub frame to select the image slices that are displayed slice_frame = Frame(sub_frame) slice_frame.grid(column=0, row=1, columnspan=self.image_nb) pic_frame = Frame(sub_frame) pic_frame.grid(column=0, row=0, columnspan=self.image_nb) self.configure_images(pic_frame, image_paths) self.add_slice_bar(slice_frame) self.set_optional_buttons(supervoxel_id_path) def configure_images(self, pic_frame, image_paths): """Create widgets to place images, descriptions and slice scrollers.""" # create a sub frame to display the images self._imagelabel = [None for _ in range(self.image_nb)] self._image_view = [None for _ in range(self.image_nb)] _descrlabel = [None for _ in range(self.image_nb)] # descriptions of the images defaults to their path basenames descriptions = [os.path.basename(image_paths[i]) for i in range(self.image_nb)] self.histogram_sliders = [] for i in range(self.image_nb): _sub_pic_frame = Frame(pic_frame) _sub_pic_frame.grid(column=int(i%2), row=int(i/2), pady=5) # set Label for a description above the images _descrlabel[i] = Label(_sub_pic_frame, text=descriptions[i]) _descrlabel[i].grid(column=0, row=0) # set Label to depict the images self._imagelabel[i] = Label(_sub_pic_frame) self._imagelabel[i].grid(column=0, row=1) # set Scales for the intensity slides _sub_sub_frame = Frame(_sub_pic_frame) _sub_sub_frame.grid(column=0, row=2) min_val = np.min(self.images_original[i]) max_val = np.max(self.images_original[i]) intensity_scale1 = Scale(_sub_sub_frame, from_=min_val, to=max_val/2, orient=HORIZONTAL) intensity_scale1.set(min_val) intensity_scale1.grid(column=0, row=0, sticky=['e', 'w', 's']) intensity_scale2 = Scale(_sub_sub_frame, from_=max_val/2, to=max_val, orient=HORIZONTAL) intensity_scale2.set(max_val) intensity_scale2.grid(column=1, row=0, sticky=['e', 'w', 's']) self.histogram_sliders.append(intensity_scale1) self.histogram_sliders.append(intensity_scale2) intensity_scale1.bind("<B1-Motion>", self.change_intensity) intensity_scale2.bind("<B1-Motion>", self.change_intensity) # Attach commands to the image frames self._imagelabel[i].bind("<Button-1>", self.click_image) self._imagelabel[i].bind("<Button-3>", self.label_dropdown) self._imagelabel[i].bind("<Button 4>", self.slice_up) self._imagelabel[i].bind("<Button 5>", self.slice_down) self._imagelabel[i].bind("<B1-Motion>", self.motion_image) self._imagelabel[i].bind("<Double-Button-1>", self.select_connected) def add_slice_bar(self, slice_frame): """Add a slice selection options to slice_frame. Returns ------- this_slice_label_number : Label the field displaying the current slice number this_new_slice : Entry the field allowing the user to fill in a slice number new_value : int the initial slice number to be set """ # Have a line displaying slice number _slice_label = Label(slice_frame, text='Slice displayed : ') _slice_label.grid(column=0, row=0, sticky=['w', 'e']) this_slice_label_number = Label(slice_frame) this_slice_label_number.grid(column=1, row=0, sticky=['w', 'e']) # Allow to change slice number _goto = Label(slice_frame, text=' - go to slice :') _goto.grid(column=2, row=0, sticky=['w', 'e']) this_new_slice = Entry(slice_frame, width=6) this_new_slice.bind('<Return>', self.goto_slice) this_new_slice.bind('<KP_Enter>', self.goto_slice) this_new_slice.grid(column=3, row=0, sticky=['w', 'e']) self.image_scale = (self.screen_width - 200) / 4 # Allow to scroll through the slices self._slice_scroll = lsb.LinkedScrollBar(master=slice_frame, command=self.disp_im, minVal=self._get_min_slice(), maxVal=self._get_max_slice(), step=1, orient='horizontal') self._slice_scroll.grid(column=0, row=1, columnspan=self.image_nb, sticky=['e', 'w', 's']) self._slice_label_number = this_slice_label_number self._new_slice = this_new_slice self.reset_slice_scroll() ##### allow to show segmentations, change axis and quit ######### def set_optional_buttons(self, supervoxel_id_path=None): """ Set bottoms to quit, change axis, show and hide segmentation, ... at the upper row of the main frame. """ _sub_frame1 = Frame(self) _sub_frame1.grid(column=0, row=0, padx=10, sticky=['n', 'e', 'w']) _sub_sub_frame1 = Frame(_sub_frame1) _sub_sub_frame1.grid(column=0, row=0, sticky=['e', 'w'], pady=10) ind = 0 self.button_axis = Button(_sub_sub_frame1, text="Change axis", command=self.change_axis) self.button_axis.grid(column=0, row=ind, pady=3) ind += 1 self.button_axis = Button(_sub_sub_frame1, text="Mirror Images", command=self.mirror_image) self.button_axis.grid(column=0, row=ind, pady=3) ind = 0 if self.segm is not None: _sub_sub_frame2 = Frame(_sub_frame1) _sub_sub_frame2.grid(column=0, row=1, sticky=['e', 'w'], pady=10) _sub_sub_frame3 = Frame(_sub_frame1) _sub_sub_frame3.grid(column=0, row=2, sticky=['e', 'w'], pady=10) self.button_supervoxels = Button(_sub_sub_frame2, text="Show/Hide supervoxels", command=self.change_supervoxels) self.button_supervoxels.grid(column=0, row=ind, sticky=['w', 'n', 'e']) if supervoxel_id_path is None: self.button_supervoxels['state'] = 'disabled' ind += 1 self.tumor_checkbox_label = Label(_sub_sub_frame2, text="Display tumor type: ", relief=FLAT) self.tumor_checkbox_label.grid(column=0, row=ind) ind += 1 self.tumor_cb = [] for i in range(len(labels)): tumor_button = IntVar() this_text = '%s (%s <%s>)' % (labels[i], label_colors_d[i], label_hot_keys[i+1]) button = Checkbutton(_sub_sub_frame2, text=this_text, variable=tumor_button, command=lambda arg0=i: self.change_segm(arg0)) button.grid(column=0, row=ind, sticky=['w', 'n', 'e']) self.tumor_cb.append(tumor_button) ind += 1 self.all_tumor_bc = IntVar() button = Checkbutton(_sub_sub_frame2, text="All tumors", variable=self.all_tumor_bc, command=lambda : self.change_segm(3)) button.grid(column=0, row=ind, sticky=['w', 'n', 'e']) ind += 1 self.no_tumor_bc = IntVar() button = Checkbutton(_sub_sub_frame2, text="No tumors", variable=self.no_tumor_bc, command=lambda : self.change_segm(4)) button.grid(column=0, row=ind, sticky=['w', 'n', 'e']) ind += 1 alpha_label = Label(_sub_sub_frame2, text="Opacity:") alpha_label.grid(column=0, row=ind) self.alpha_scale = Scale(_sub_sub_frame2, from_=0.0, to=1.0, command=self.set_alpha, orient=HORIZONTAL) ind += 1 self.alpha_scale.set(self.alpha) self.alpha_scale.grid(column=0, row=ind, columnspan=self.image_nb, sticky=['w', 'n', 'e']) ind = 0 self.button_save_segm = Button(_sub_sub_frame3, text="Save segmentation", command=self.save_segm) self.button_save_segm.grid(column=0, row=ind, sticky=['w', 'n', 'e']) ind += 1 self.button_open_segm = Button(_sub_sub_frame3, text="Open segmentation", command=self.open_segm) self.button_open_segm.grid(column=0, row=ind, sticky=['w', 'n', 'e']) #################### Display images ########################### def disp_im(self): """use the size, slice and zoom to display the image""" self.focus_set() slice_index = int(self._slice_scroll.val) for i in range(self.image_nb): pix = np.array(\ ms.get_slice(self.images[i], boundaries=self.boundaries, visual_center=slice_index, axis=self._axis), dtype='float') temp_im, im_size = pil.get_image_pil(pix, self.image_scale, return_image_size=True) self.image_size = im_size temp_im = temp_im.convert('RGB') if self.segm is not None: temp_im = self.add_segmentation(temp_im, slice_index) if self.show_supervoxels and type(self.supervoxel_border) is np.ndarray: temp_im = self.add_supervoxels(temp_im, slice_index) # create the 2d view with or without the bounding box self._image_view[i] = ImageTk.PhotoImage(temp_im) self._imagelabel[i]['image'] = self._image_view[i] # update slice label self._slice_label_number['text'] = str(int(self._slice_scroll.val)) def add_segmentation(self, image, slice_ind=None): """ Add a segmentation to the image (colors will be overlaid). """ #Segmentation is separated into binary segmentations #Also, segm contains 3 binary segmentations if len(self.selected_id_list) > 0: segm = utils.get_separate_labels(self.segm_disp, length=len(labels)+1) else: segm = utils.get_separate_labels(self.segm, length=len(labels)) r, g, b = image.convert('RGB').split() rgb = [r, g, b] #Variable that contains selected colors colors = [None for _ in range(len(segm))] #Add labels if their check box is selected if hasattr(self, 'tumor_cb'): for i in range(len(labels)): if self.tumor_cb[i].get() == 1: colors[i] = label_colors[i] if len(colors)==4: colors[3] = selected #Do the painting with respect to colors variable for i in range(len(segm)): if colors[i] is None: continue pix = np.array(ms.get_slice(segm[i], boundaries=self.boundaries, visual_center=slice_ind, axis=self._axis)) pix[pix > 0.5] = 1 pix = pix.astype('uint8') if np.any(pix): color_region = pil.get_image_pil(pix, self.image_scale) rgb = pil.set_color_custom(color_region, colors[i], rgb=rgb) #Merge operation for all segm_im = ImagePIL.merge("RGB", rgb) return ImagePIL.blend(image, segm_im, self.alpha) def add_supervoxels(self, image, slice_ind=None): """ Add supervoxels to the image (boundaries will be drawn).""" r, g, b = image.convert('RGB').split() pix = np.array(ms.get_slice(self.supervoxel_id, boundaries=self.boundaries, visual_center=slice_ind, axis=self._axis)) self.supervoxel_id_slice = pix if np.any(pix): pix = pix *50000 #Get image with supervoxels that has borders calculated with contours sup_im = pil.get_image_pil(pix, self.image_scale)\ .convert("RGB")\ .filter(ImageFilter.CONTOUR)\ .filter(ImageFilter.EDGE_ENHANCE_MORE)\ .filter(ImageFilter.EDGE_ENHANCE_MORE) r_border, g_border, b_border = sup_im.split() r = pil.set_color_empty(r, r_border) g = pil.set_color_empty(g, g_border) b = pil.set_color_empty(b, b_border) #Merge operation for all colors sup_im = ImagePIL.merge("RGB", (r, g, b)) return ImagePIL.blend(image, sup_im, self.alpha) #################### EVENTS: Change slice ###################### def reset_slice_scroll(self): max_slice = self._get_max_slice() min_slice = self._get_min_slice() diff = max_slice - min_slice new_value = int(min_slice + np.floor(diff / 2)) self._slice_scroll.set_value(new_value) def slice_up(self, *args): """increase slice number""" new_value = self._slice_scroll.val + 1 if new_value <= self._get_max_slice(in_boundaries=True): self._slice_scroll.set_value(new_value) def slice_down(self, *args): """decrease slice number""" new_value = self._slice_scroll.val - 1 if new_value >= self._get_min_slice(in_boundaries=True): self._slice_scroll.set_value(new_value) def _get_max_slice(self, in_boundaries=True): """Gets the maximum slice of the numpy array.""" if in_boundaries: return self.boundaries[self._axis][1] else: return self.dim[self._axis] - 1 def _get_min_slice(self, in_boundaries=True): """Gets the minimum slice of the numpy array.""" if in_boundaries: return self.boundaries[self._axis][0] else: return 0 def change_axis(self): """It changes the axis of the image.""" self._axis = (self._axis + 1) % 3 self.reset_slice_scroll() self.disp_im() def mirror_image(self): """It mirrors the image.""" for i in range(self.image_nb): self.images[i] = np.flip(self.images[i], axis=(self._axis + 1) % 3) self.images_original[i] = np.flip(self.images_original[i], axis=(self._axis + 1) % 3) if self.segm is not None: self.segm = np.flip(self.segm, axis=(self._axis + 1) % 3) if self.show_supervoxels and type(self.supervoxel_border) is np.ndarray: self.supervoxel_id = np.flip(self.supervoxel_id, axis=(self._axis + 1) % 3) self.boundaries = ms.get_boundaries_series(self.images) self.disp_im() def goto_slice(self, *args): """moves to the desired slice""" z = self._new_slice.get() max_valid_z = self._get_max_slice(in_boundaries=True) min_valid_z = self._get_min_slice(in_boundaries=True) # if not an integer try: z = int(z) # if out of range if z < 0: msg = 'Please select a positive slice index. ' +\ 'Lowest non-zero slice is shown.' self._slice_scroll.set_value(min_valid_z) self._new_slice.delete(0, END) elif z < min_valid_z: msg = 'Slice %d has only zeros. ' % z msg += 'Lowest non-zero slice is shown.' self._slice_scroll.set_value(min_valid_z) self._new_slice.delete(0, END) elif z > self._get_max_slice(in_boundaries=False): msg = 'Slice %d exceeds the image dimension. ' % z msg += 'Highest non-zero slice is shown.' self._slice_scroll.set_value(max_valid_z) self._new_slice.delete(0, END) elif z > max_valid_z: msg = 'Slice %d consists of zeros. ' % z msg += 'Highest non-zero slice is shown.' self._slice_scroll.set_value(max_valid_z) self._new_slice.delete(0, END) else: self._slice_scroll.set_value(z) self._new_slice.delete(0, END) except ValueError as e: print e self._new_slice.delete(0, END) ############### EVENTS : select and change ######################### def click_image(self, event): """ Select a supervoxel and color it in blue. Called when clicked on an image. """ supervoxel_id = self._get_supervoxel_id(event.x, event.y) self._update_selected_id_list(supervoxel_id) def motion_image(self, event): """ Select multiple supervoxels and color it in blue. Called when clicked and motioned on an image. """ supervoxel_id = self._get_supervoxel_id(event.x, event.y) self._update_selected_id_list(supervoxel_id, unselect=False) def select_connected(self, event): supervoxel_id = self._get_supervoxel_id(event.x, event.y) label = self.segm[self.supervoxel_id == supervoxel_id][0] im = sitk.GetImageFromArray((self.segm==label).astype(np.int)) connected = sitk.GetArrayFromImage(sitk.ConnectedComponent(im)) clabel = connected[self.supervoxel_id == supervoxel_id][0] supervoxel_ids = list(np.unique(self.supervoxel_id[connected==clabel])) self._update_selected_id_list(supervoxel_ids) def unselect_all(self, *args): """ Unselect all selected supervoxels. """ print 'Emptying selection' self.selected_id_list = [] self.disp_im() def set_healthy(self, *args): """ Select all supervoxels in this slice and set them to background. """ print 'Setting all supervoxels in slice to background' supervoxel_ids = list(np.unique(self.supervoxel_id_slice)) if 0 in supervoxel_ids: supervoxel_ids.remove(0) self.change_label(0, supervoxel_ids=supervoxel_ids) def inverse_selection(self, *args): all_ids = np.unique(self.supervoxel_id) new_ids = [sup_id for sup_id in all_ids if id not in self.selected_id_list] self.selected_id_list = new_ids self.update_segm_display() def _get_supervoxel_id(self, eventx, eventy): """ Get the supervoxel id of the given mouse position. """ #the size of the image: #0 element of image corresponds to 1 element #of supervoxel_id_slice and vice versa #converting the coordinates on the clicked image into the #coordinates of the voxel #Since the displayed pixels range from 1 to lenght+1, #need to subtract 1 from the coordinates # print self.supervoxel_id_slice.shape x_coordinate_supervoxel = (eventx - 1) *\ len(self.supervoxel_id_slice[0]) /\ self.image_size[0] y_coordinate_supervoxel = (eventy - 1) *\ len(self.supervoxel_id_slice) /\ self.image_size[1] z_coordinate_supervoxel = int(self._slice_scroll.val) coordinates = [x_coordinate_supervoxel, y_coordinate_supervoxel, z_coordinate_supervoxel] selected_id = self.supervoxel_id_slice[coordinates[1], coordinates[0]] return selected_id def _update_selected_id_list(self, selected_id, add_to_undo=True, unselect=True): """ Depending whether this supervoxel is already selected, mark or unmark the supervoxel. """ if isinstance(selected_id, list): if unselect: if all([sid in self.selected_id_list for sid in selected_id]): map(self.selected_id_list.remove, selected_id) else: self.selected_id_list.extend(selected_id) else: if not all([sid in self.selected_id_list for sid in selected_id]): self.selected_id_list.extend(selected_id) else: return #nothing changed else: if unselect: if selected_id in self.selected_id_list: self.selected_id_list.remove(selected_id) else: self.selected_id_list.append(selected_id) else: if selected_id not in self.selected_id_list: self.selected_id_list.append(selected_id) else: return #nothing changed print 'Selected supervoxels: %s' % str(self.selected_id_list) if add_to_undo: action = selected_id, None, None self.undo_stack.append(action) self.update_segm_display() def update_segm_display(self): """ Color all supervoxels with ids in self.selected_id_list blue in self.segm_disp. """ #Selection is updated for visualisation self.segm_disp = self.segm.copy() if len(self.selected_id_list) == 1: mask = self.supervoxel_id == self.selected_id_list[0] elif len(self.selected_id_list) > 1: ind1d = np.in1d(self.supervoxel_id.flatten(), self.selected_id_list) mask = np.reshape(ind1d, self.supervoxel_id.shape) else: mask = np.zeros_like(self.supervoxel_id) selected_idx = np.where(mask) self.segm_disp[selected_idx[0], selected_idx[1], selected_idx[2]] = 4 self.disp_im() def print_com(self, *args): ind1d = np.in1d(self.supervoxel_id.flatten(), self.selected_id_list) ind3d = np.reshape(ind1d, self.supervoxel_id.shape) com = spim.measurements.center_of_mass(ind3d) print 'Center of mass' print '\t com : %s' % str(com) if self.segm_path is not None: ID = os.path.basename(self.segm_path).split('.')[0] print '\t path : %s' % ID print 'com_dict["%s"] = %s' % (ID, str(com)) def label_dropdown(self, event): """When clicking right on a supervoxel, give a drop-down list with labels to change to.""" if len(self.selected_id_list) == 0: return # Adding the selection menu with radio buttons. self.menu = Menu(self, tearoff=0) self.menu\ .add_radiobutton(label="Background", value=0, command=lambda arg0=0: \ self.change_label(arg0)) self.menu\ .add_radiobutton(label=labels[0], value=3, command=lambda arg0=1: \ self.change_label(arg0)) self.menu\ .add_radiobutton(label=labels[1], value=2, command=lambda arg0=2: \ self.change_label(arg0)) self.menu\ .add_radiobutton(label=labels[2], value=1, command=lambda arg0=3: \ self.change_label(arg0)) self.menu.tk_popup(event.x_root, event.y_root) def change_label(self, new_label, supervoxel_ids=None): """Changing the label of the supervoxel new label is the type of pixel 0 - background, #3 - edema, 2 - Non-active tumor and 1 - Active tumor self.segm is the array with segmentations self.supervoxel_ids is the the array with the supervoxels """ empty_selected_id_list = False if supervoxel_ids is None: empty_selected_id_list = True supervoxel_ids = self.selected_id_list #Empty the redo_stack whenever segmentation is changed by user self.redo_stack = [] for selected_id in (supervoxel_ids): #Segmentation is updated with new labels #Old label is saved for undo and redo actions region = self.supervoxel_id == selected_id old_label = self.segm[region] old_label = old_label[0] self.segm[region] = new_label # if the checkboxes are unselected but changed, they are automatically checked. if new_label > 0: self.tumor_cb[new_label-1].set(1) self.no_tumor_bc.set(0) #Actions as coordinates, old_label and new_label to go switch between segmentation states action = selected_id, old_label, new_label self.undo_stack.append(action) if empty_selected_id_list: self.selected_id_list = [] self.disp_im() def change_intensity(self, event): """ Change the intensity of the images based on the hist slider.""" slider = event.widget i = self.histogram_sliders.index(slider) # find the image index based on the slider image_i = 3 if i == 0 or i == 1: image_i = 0 elif i == 2 or i == 3: image_i = 1 elif i == 4 or i == 5: image_i = 2 original_image = self.images_original[image_i] image = self.images[image_i] if (i + 1) % 2 == 0: slider_upper = slider slider_lower = self.histogram_sliders[i - 1] else: slider_lower = slider slider_upper = self.histogram_sliders[i + 1] lower = slider_lower.get() upper = slider_upper.get() lower = np.float(lower) upper = np.float(upper) np.clip(original_image, lower, upper, image) np.subtract(image, lower, out=image, casting='unsafe') self.disp_im() def change_segm(self, button_ind): """ Show or hide the segmentation from the images.""" # if tumor type is (un)selected, set all_tumors and no_tumors # accordingly if button_ind <= 2: checked = [self.tumor_cb[i].get() for i in range(len(labels))] checked_sum = sum(checked) if checked_sum < len(labels): self.all_tumor_bc.set(0) else: self.all_tumor_bc.set(1) if checked_sum > 0: self.no_tumor_bc.set(0) else: self.no_tumor_bc.set(1) # if all_tumors or no_tumors is (un)selected, allign the other # buttons if button_ind == 3: for i in range(len(labels)): self.tumor_cb[i].set(1) self.all_tumor_bc.set(1) self.no_tumor_bc.set(0) if button_ind == 4: for i in range(len(labels)): self.tumor_cb[i].set(0) self.all_tumor_bc.set(0) self.no_tumor_bc.set(1) self.disp_im() def toggle_segm(self, *args): # if at least one tumor type is visualized, # remove all tumors (set no_tumors) and supervoxels checked = [self.tumor_cb[i].get() for i in range(len(labels))] checked_sum = sum(checked) if checked_sum > 0: print 'Visualizing no tumors' self.change_segm(4) # if no tumor type is visualized, set all_tumors # visualize all tumors (set all_tumors) and supervoxels else: print 'Visualizing all tumors' self.change_segm(3) def change_supervoxels(self): """ Toggle between showing supervoxels or not.""" self.show_supervoxels = not self.show_supervoxels self.disp_im() def set_alpha(self, *args): """ Change the opacity level.""" self.alpha = self.alpha_scale.get() self.disp_im() def undo_action(self, *args): """ Execute undo action on CTRL-Z.""" #If respective stack is not empty #Go to the last state of segmentation #and update the redo_stack print 'undo!' if len(self.undo_stack) > 0: action = self.undo_stack.pop() self.redo_stack.append(action) selected_id, old_label, _ = action if old_label is not None: self._change_label_supervoxel(old_label, selected_id) else: self._update_selected_id_list(selected_id, add_to_undo=False) self.disp_im() def redo_action(self, *args): """Execute redo action on CTRL-Y""" #If respective stack is not empty #Go to the last state of segmentation #and update the undo_stack print 'redo!' if len(self.redo_stack) > 0: action = self.redo_stack.pop() self.undo_stack.append(action) selected_id, _, new_label = action if new_label is not None: self._change_label_supervoxel(new_label, selected_id) else: self._update_selected_id_list(selected_id, add_to_undo=False) self.disp_im() def _change_label_supervoxel(self, new_label, selected_id): """ Change the label of the supervoxel cooordinates is a list of the ids of the selected voxel. coordinates[0] - x coordinate, coordinate[1] - y coordinate, coordinate[2] - z coordinate new label is the type of pixel 0 - background, 3 - edema, 2 - Non-active tumor and 1 - Active tumor self.segms is the array with segmentations self.supervoxel_ids is the the array with the supervoxels""" #Segmentation is updated with new labels self.segm[self.supervoxel_id == selected_id] = new_label self.disp_im() #################### Save or load a segmentation ################### def save_segm(self): """ Save the current custom segmentation into a file.""" kwargs = {} if self.segm_path is not None: dirname, basename = os.path.split(self.segm_path) if not basename.startswith('corrected_'): basename = 'corrected_'+basename kwargs['initialfile'] = basename kwargs['initialdir'] = dirname path = asksaveasfilename(title="Please select a path to save your segmentation", filetypes=[('Image files', ('.nii', '.mha', '.nii.gz'))], **kwargs) #TODO: change here self.segm_path if self.segm_path is not None: old_segm = oitk.get_itk_image(self.segm_path) image = oitk.make_itk_image(self.segm, old_segm) oitk.write_itk_image(image, path) def open_segm(self): """ Open a new custom segmentation from a file.""" kwargs = {} kwargs['initialdir'] = os.environ.get('HOME') if self.segm_path is not None: dirname, basename = os.path.split(self.segm_path) if not basename.startswith('corrected_'): basename = 'corrected_'+basename kwargs['initialfile'] = basename kwargs['initialdir'] = dirname msg = 'Please select a segmentation' segm_path = askopenfilename(title=msg, filetypes=[('Image files', ('.nii', '.mha', '.nii.gz'))], **kwargs) if os.path.exists(segm_path): self.segm_path = segm_path self.segm = oitk.get_itk_array(self.segm_path) self.disp_im()
class Gamelist(): def __init__(self, drive, platform): GameListData.game_list_data_json = GameListData().get_game_list() self.json_game_list_data = GameListData.game_list_data_json if drive == 'USB(*)': self.drive_to_show = '/dev_' + drive.lower().replace('(*)', '') else: self.drive_to_show = '/dev_' + drive.lower() + '/' self.platform_to_show = platform + '_games' self.WCM_BASE_PATH = AppPaths.wcm_gui self.last_selection = (None, 0) self.list_of_items = [] self.selected_title_id = None self.selected_title = None self.selected_path = None self.selected_filename = None self.drive_system_path_array = None self.is_cleared = False def create_main_frame(self, entry_field_title_id, entry_field_title, entry_field_filename, entry_field_iso_path, entry_field_platform, drive_system_array): self.entry_field_title_id = entry_field_title_id self.entry_field_title = entry_field_title self.entry_field_filename = entry_field_filename self.entry_field_iso_path = entry_field_iso_path self.entry_field_platform = entry_field_platform self.drive_system_path_array = drive_system_array self.corrected_index = [] self.main_frame = Frame() self.popup_menu = Menu(self.main_frame, tearoff=0) self.popup_menu.add_command(label="Delete", command=self.delete_selected) self.popup_menu.add_command(label="Rename", command=self.rename_selected) # self.popup_menu.add_command(label="Refetch", # command=self.refetch) # self.popup_menu.add_command(label="Select All", # command=self.select_all) s = Scrollbar(self.main_frame) self._listbox = Listbox(self.main_frame, width=465) self._listbox.bind('<Enter>', self._bound_to_mousewheel) self._listbox.bind('<Leave>', self._unbound_to_mousewheel) self._listbox.bind("<Button-3>", self.popup) # Button-2 on Aqua s.pack(side=RIGHT, fill=Y) self._listbox.pack(side=LEFT, fill=Y) s['command'] = self._listbox.yview self._listbox['yscrollcommand'] = s.set # default filters if 'ALL_games' == self.platform_to_show: # iterate all platforms for platform in self.json_game_list_data: for list_game in self.json_game_list_data[platform]: # titles in the list has been designed to be unique if '/dev_all/' == self.drive_to_show or self.drive_to_show in list_game['path']: self.add_item(list_game['title']) else: for list_game in self.json_game_list_data[self.platform_to_show]: if '/dev_all/' == self.drive_to_show or self.drive_to_show in list_game['path']: self.add_item(list_game['title']) for x in range(19 - self._listbox.size()): self.add_item('') # adding shade to every other row of the list for x in range(0, self._listbox.size()): if x % 2 == 0: self._listbox.itemconfig(x, {'fg': 'white'}, background='#001738') else: self._listbox.itemconfig(x, {'fg': 'white'}, background='#001F4C') self.label = Label(self.main_frame) self.selection_poller() return self.main_frame def selection_poller(self): self.label.after(200, self.selection_poller) self.new_selection = self._listbox.curselection() # cursor har been initiated if self._listbox.curselection() is not (): if self.new_selection[0] is not self.last_selection[0] or self.is_cleared: self.entry_fields_update(self.new_selection) self.is_cleared = False self.last_selection = self.new_selection def entry_fields_update(self, new_selection): for platform in self.json_game_list_data: for list_game in self.json_game_list_data[platform]: self.selected_title = self._listbox.get(new_selection[0]) tmp_title = list_game['title'] match = self.selected_title == str(tmp_title) if match: self.selected_title_id = str(list_game['title_id']).replace('-', '') self.selected_title = str(list_game['title']) self.selected_path = str(list_game['path']) self.selected_filename = str(list_game['filename']) self.selected_platform = str(list_game['platform']) # parse drive and system from json data path_array = filter(None, self.selected_path.split('/')) self.drive_system_path_array[0] = path_array[0] self.drive_system_path_array[1] = path_array[1] self.drive_system_path_array[2] = '/'.join(path_array[2:len(path_array)]).replace('//', '') self.entry_field_title_id.delete(0, len(self.entry_field_title_id.get())-1) self.entry_field_title_id.delete(0, END) self.entry_field_title_id.insert(0, self.selected_title_id) self.entry_field_title.delete(0, END) self.entry_field_title.insert(0, self.selected_title) self.entry_field_filename.delete(0, END) self.entry_field_filename.insert(0, self.selected_filename) self.entry_field_platform.delete(0, END) self.entry_field_platform.insert(0, self.selected_platform) return True def get_selected_path(self): return self.current_iso_path def get_listbox(self): return self._listbox def get_ascending_index(self, list_of_items, item, ignore_case=True): lo = 0 hi = len(list_of_items) if ignore_case: item = item.lower() while lo < hi: mid = (lo + hi) // 2 if item < list_of_items[mid].lower(): hi = mid else: lo = mid + 1 else: while lo < hi: mid = (lo + hi) // 2 if item < list_of_items[mid]: hi = mid else: lo = mid + 1 return lo def add_item(self, item): if item != '': self.list_of_items = self._listbox.get(0, END) # getting ascending index in order to sort alphabetically index = self.get_ascending_index(self.list_of_items, item) self._listbox.insert(index, item) else: self._listbox.insert(END, item) def get_items(self): return self.list_of_items def _bound_to_mousewheel(self, event): self._listbox.bind_all("<MouseWheel>", self._on_mousewheel) def _unbound_to_mousewheel(self, event): self._listbox.unbind_all("<MouseWheel>") def _on_mousewheel(self, event): self._listbox.yview_scroll(int(-1*(event.delta/30)), "units") def popup(self, event): try: self._listbox.selection_clear(0, END) self._listbox.selection_set(self._listbox.nearest(event.y)) self._listbox.activate(self._listbox.nearest(event.y)) finally: if self._listbox.get(self._listbox.curselection()[0]) is not '': self.popup_menu.tk_popup(event.x_root + 43, event.y_root + 12, 0) self.popup_menu.grab_release() self.popup_menu.focus_set() def delete_selected(self): import tkMessageBox game_folder_path = os.path.join(AppPaths.game_work_dir, '..') response = tkMessageBox.askyesno('Delete game folder', 'Delete \'' + self.entry_field_title.get() + '\'?\n\nFolder path: ' + os.path.realpath(game_folder_path)) # yes if response: # remove game from visual game list for i in self._listbox.curselection()[::-1]: self._listbox.delete(i) removed_index = i # remove game from json game list platform_key = self.entry_field_platform.get() + '_games' self.json_game_list_data[platform_key] = [x for x in self.json_game_list_data[platform_key] if x['title'] != self.selected_title] # update the json game list file with open(GameListData.GAME_LIST_DATA_PATH, 'w') as newFile: json_text = json.dumps(self.json_game_list_data, indent=4, separators=(",", ":")) newFile.write(json_text) # remove the game build folder too if AppPaths.game_work_dir != os.path.join(AppPaths.wcm_gui, 'work_dir'): if os.path.isdir(game_folder_path): if 'webman-classics-maker' in game_folder_path: shutil.rmtree(game_folder_path) # clear entry_fields self.clear_entries_and_path() # set cursor self._listbox.select_set(removed_index) #This only sets focus on the first item. def rename_selected(self): self.entry_field_title.selection_range(0, END) self.entry_field_title.focus_set() def select_all(self): self._listbox.selection_set(0, 'end') def clear_entries_and_path(self): self.entry_field_title_id.delete(0, len(self.entry_field_title_id.get())-1) self.entry_field_title_id.delete(0, END) self.entry_field_title.delete(0, END) self.entry_field_platform.delete(0, END) self.entry_field_filename.delete(0, END) self.is_cleared = True def get_selected_build_dir_path(self): self.build_dir_path = '' if self.selected_filename not in {'', None}: filename = self.selected_filename title_id = self.selected_title_id.replace('-', '') build_base_path = AppPaths.builds tmp_filename = filename # removes the file extension from tmp_filename for file_ext in GlobalVar.file_extensions: if filename.upper().endswith(file_ext): tmp_filename = filename[0:len(filename)-len(file_ext)] break game_folder_name = tmp_filename.replace(' ', '_') + '_(' + title_id.replace('-', '') + ')' self.build_dir_path = os.path.join(build_base_path, game_folder_name) return self.build_dir_path
class PmaConvert: def __init__(self, config): root = Tk() # Root Definition root.geometry('1100x700') root.title('PMA Convert') # Configuration and Variables self.file_selection = '' self.is_converting = False self.options = config['option_definitions'] gui_config = config['gui_config'] # UI ## Frames self.main_frame = Frame(root) self.position_main_frame(gui_config['screen_orientation']) ## Components self.log = Text(self.main_frame, bd=1, relief=SUNKEN, width=140, height=23, state=DISABLED, spacing3=1, wrap=WORD) choose_text = ('1. Choose XLSForm (.xls or .xlsx) file(s) for ' 'conversion.') self.choose_files_label = Label(self.main_frame, text=choose_text) # TODO: Get spacing to work. # self.choose_files_label.grid(row=3, column=3, padx=(50, 50)) # self.choose_files_label.grid(row=3, column=3, pady=(50, 50)) self.choose_files_label.pack() self.choose_files_button = Button(self.main_frame, text='Choose file...', fg='black', command=self.on_open) self.choose_files_button.pack() out_text = 'Choose location for output file(s).' self.output_location_label = Label(self.main_frame, text=out_text) self.output_location_button = Button(self.main_frame, text='Choose location...', fg='black') if gui_config['output_location_on'] is True: self.output_location_label.pack() self.output_location_button.pack() self.choose_options_label = Label(self.main_frame, text='2. Choose conversion options.') self.choose_options_label.pack() ### Create Options Checkboxes # Task: Dynamically generate: http://stackoverflow.com/questions/... # ...553784/can-you-use-a-string-to-instantiate-a-class-in-python self.preexisting = BooleanVar() pre_text = self.options['preexisting']['label'] self.preexisting_opt = Checkbutton(self.main_frame, text=pre_text, variable=self.preexisting) self.preexisting_opt.pack() self.regular = BooleanVar() reg_text = self.options['regular']['label'] self.regular_opt = Checkbutton(self.main_frame, text=reg_text, variable=self.regular) self.regular_opt.pack() self.novalidate = BooleanVar() noval_text = self.options['novalidate']['label'] self.novalidate_opt = Checkbutton(self.main_frame, text=noval_text, variable=self.novalidate) self.novalidate_opt.pack() self.ignore_version = BooleanVar() ig_text = self.options['ignore_version']['label'] self.ignore_version_opt = Checkbutton(self.main_frame, text=ig_text, variable=self.ignore_version) self.ignore_version_opt.pack() self.linking_warn = BooleanVar() link_text = self.options['linking_warn']['label'] self.linking_warn_option = Checkbutton(self.main_frame, text=link_text, variable=self.linking_warn) self.linking_warn_option.pack() self.debug = BooleanVar() debug_text = self.options['debug']['label'] self.debug_option = Checkbutton(self.main_frame, text=debug_text, variable=self.debug) self.debug_option.pack() self.extras = BooleanVar() extras_text = self.options['extras']['label'] self.extras_option = Checkbutton(self.main_frame, text=extras_text, variable=self.extras) self.extras_option.pack() self.convert_label = Label(self.main_frame, text='3. Run conversion.') self.convert_label.pack() # Task: Add xscrollcommand and yscrollcommand. self.convert_button = Button(self.main_frame, text='Convert', fg='black', command=self.convert) self.convert_button.pack() self.log.pack(fill=X, expand=1) self.log_text('PMA Convert allows you to convert .xls or .xlsx form ' 'definition files to files which are compatible with ODK ' 'Collect.\n\nIf you need to copy and paste from this ' 'log, highlight the text and press CTRL+C to copy. Then ' 'press CTRL+V to paste.\n\n' '====================================================\n\n' 'Awaiting file selection.') # Task: Fix menus. They're not working. self.context_menu = Menu(self.main_frame, tearoff=0) self.context_menu.add_command(label="Convert", command=self.convert) self.main_frame.bind("<Button-3>", self.popup) # - Note: Strangely this stopped anchoring to bottom suddenly, for some # reason. So it is temporarily disabled. self.status_bar = Label(self.main_frame, text='Awaiting file selection.', bd=1, relief=SUNKEN, anchor=W) if gui_config['status_bar_on'] is True: self.status_bar.pack(side=BOTTOM, fill=X) # Run root.mainloop() # Functions def popup(self, event): # Note: Currently doesn't work. self.context_menu.post(event.x_root, event.y_root) # display the popup menu try: self.context_menu.tk_popup(event.x_root, event.y_root, 0) finally: # make sure to release the grab (Tk 8.0a1 only) self.context_menu.grab_release() def position_main_frame(self, orientation): if orientation == 'center': x, y, a = .5, .5, 'c' return self.main_frame.place(relx=x, rely=y, anchor=a) elif orientation == 'top': return self.main_frame.pack() else: return self.main_frame.pack() def on_open(self): file_types = [ ('XLS Files', '*.xls'), ('XLSX Files', '*.xlsx'), ('All files', '*') ] try: self.file_selection = tkFileDialog.askopenfilename( filetypes=file_types, title='Open one or more files.', message='Open one or more files', multiple=1 ) except: self.file_selection = tkFileDialog.askopenfilename( filetypes=file_types, title='Open one or more files.', multiple=1 ) if self.file_selection != '': self.set_status('Click on Convert to convert files.') log_output = 'Ready for conversion: \n' for file in self.file_selection: log_output += '* ' + str(file) + '\n' log_output = log_output[:-1] # Removes the last '\n'. self.log.configure(self.log_text(log_output)) def set_status(self, new_status): self.status_bar.configure(text=new_status) def log_text(self, new_text): self.log.configure(state=NORMAL) self.log.insert(END, str(new_text) + '\n\n') self.log.configure(state=DISABLED) self.log.bind("<1>", lambda event: self.log.focus_set()) def convert(self): if self.file_selection != '': f = self.file_selection kwargs = { SUFFIX: u'', PREEXISTING: self.preexisting.get(), PMA: not self.regular.get(), CHECK_VERSIONING: not self.ignore_version.get(), STRICT_LINKING: not self.linking_warn.get(), VALIDATE: not self.novalidate.get(), EXTRAS: self.extras.get(), DEBUG: self.debug.get() } buffer = StringIO.StringIO() if not kwargs[DEBUG]: sys.stdout = buffer sys.stderr = buffer else: self.log_text('--> DEBUG MODE: check console output') try: xlsform_convert(f, **kwargs) except ConvertError as e: print unicode(e) except OSError as e: # Should catch WindowsError, impossible to test on Mac traceback.print_exc() print e if not kwargs[DEBUG]: sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ self.log_text(buffer.getvalue())
class LintGui(object): """Build and control a window to interact with pylint""" def __init__(self, root=None): """init""" self.root = root or Tk() self.root.title('Pylint') #reporter self.reporter = None #message queue for output from reporter self.msg_queue = Queue.Queue() self.msgs = [] self.visible_msgs = [] self.filenames = [] self.rating = StringVar() self.tabs = {} self.report_stream = BasicStream(self) self.differ = differ.Differ() #gui objects self.lbMessages = None self.showhistory = None self.results = None self.btnRun = None self.information_box = None self.convention_box = None self.refactor_box = None self.warning_box = None self.error_box = None self.fatal_box = None self.txtModule = None self.status = None self.msg_type_dict = None self.init_gui() def init_gui(self): """init helper""" #setting up frames top_frame = Frame(self.root) mid_frame = Frame(self.root) radio_frame = Frame(self.root) res_frame = Frame(self.root) msg_frame = Frame(self.root) check_frame = Frame(self.root) history_frame = Frame(self.root) btn_frame = Frame(self.root) rating_frame = Frame(self.root) top_frame.pack(side=TOP, fill=X) mid_frame.pack(side=TOP, fill=X) history_frame.pack(side=TOP, fill=BOTH, expand=True) radio_frame.pack(side=TOP, fill=BOTH, expand=True) rating_frame.pack(side=TOP, fill=BOTH, expand=True) res_frame.pack(side=TOP, fill=BOTH, expand=True) check_frame.pack(side=TOP, fill=BOTH, expand=True) msg_frame.pack(side=TOP, fill=BOTH, expand=True) btn_frame.pack(side=TOP, fill=X) # Binding F5 application-wide to run lint self.root.bind('<F5>', self.run_lint) #Message ListBox rightscrollbar = Scrollbar(msg_frame) rightscrollbar.pack(side=RIGHT, fill=Y) bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL) bottomscrollbar.pack(side=BOTTOM, fill=X) self.lbMessages = Listbox(msg_frame, yscrollcommand=rightscrollbar.set, xscrollcommand=bottomscrollbar.set, bg="white") self.lbMessages.bind("<Double-Button-1>", self.show_sourcefile) self.lbMessages.pack(expand=True, fill=BOTH) rightscrollbar.config(command=self.lbMessages.yview) bottomscrollbar.config(command=self.lbMessages.xview) #Message context menu self.mnMessages = Menu(self.lbMessages, tearoff=0) self.mnMessages.add_command(label="View in sourcefile", command=self.show_sourcefile) self.mnMessages.add_command(label="Add to ignore patchfile", command=self.add_to_ignore_patchfile) self.lbMessages.bind("<Button-3>", self.show_messages_context) #History ListBoxes rightscrollbar2 = Scrollbar(history_frame) rightscrollbar2.pack(side=RIGHT, fill=Y) bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL) bottomscrollbar2.pack(side=BOTTOM, fill=X) self.showhistory = Listbox(history_frame, yscrollcommand=rightscrollbar2.set, xscrollcommand=bottomscrollbar2.set, bg="white") self.showhistory.pack(expand=True, fill=BOTH) rightscrollbar2.config(command=self.showhistory.yview) bottomscrollbar2.config(command=self.showhistory.xview) self.showhistory.bind('<Double-Button-1>', self.select_recent_file) self.set_history_window() #status bar self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W) self.status.pack(side=BOTTOM, fill=X) #labels self.lblRatingLabel = Label(rating_frame, text='Rating:') self.lblRatingLabel.pack(side=LEFT) self.lblRating = Label(rating_frame, textvariable=self.rating) self.lblRating.pack(side=LEFT) Label(mid_frame, text='Recently Used:').pack(side=LEFT) Label(top_frame, text='Module or package').pack(side=LEFT) #file textbox self.txtModule = Entry(top_frame, background='white') self.txtModule.bind('<Return>', self.run_lint) self.txtModule.pack(side=LEFT, expand=True, fill=X) #results box rightscrollbar = Scrollbar(res_frame) rightscrollbar.pack(side=RIGHT, fill=Y) bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL) bottomscrollbar.pack(side=BOTTOM, fill=X) self.results = Listbox(res_frame, yscrollcommand=rightscrollbar.set, xscrollcommand=bottomscrollbar.set, bg="white", font="Courier") self.results.pack(expand=True, fill=BOTH, side=BOTTOM) rightscrollbar.config(command=self.results.yview) bottomscrollbar.config(command=self.results.xview) #buttons Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT) Button(top_frame, text='Open Package', command=(lambda : self.file_open(package=True))).pack(side=LEFT) self.btnRun = Button(top_frame, text='Run', command=self.run_lint) self.btnRun.pack(side=LEFT) Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM) #radio buttons self.information_box = IntVar() self.convention_box = IntVar() self.refactor_box = IntVar() self.warning_box = IntVar() self.error_box = IntVar() self.fatal_box = IntVar() i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'], variable=self.information_box, command=self.refresh_msg_window) c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'], variable=self.convention_box, command=self.refresh_msg_window) r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'], variable=self.refactor_box, command=self.refresh_msg_window) w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'], variable=self.warning_box, command=self.refresh_msg_window) e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'], variable=self.error_box, command=self.refresh_msg_window) f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'], variable=self.fatal_box, command=self.refresh_msg_window) i.select() c.select() r.select() w.select() e.select() f.select() i.pack(side=LEFT) c.pack(side=LEFT) r.pack(side=LEFT) w.pack(side=LEFT) e.pack(side=LEFT) f.pack(side=LEFT) #check boxes self.box = StringVar() # XXX should be generated report = Radiobutton(radio_frame, text="Report", variable=self.box, value="Report", command=self.refresh_results_window) rawMet = Radiobutton(radio_frame, text="Raw metrics", variable=self.box, value="Raw metrics", command=self.refresh_results_window) dup = Radiobutton(radio_frame, text="Duplication", variable=self.box, value="Duplication", command=self.refresh_results_window) ext = Radiobutton(radio_frame, text="External dependencies", variable=self.box, value="External dependencies", command=self.refresh_results_window) stat = Radiobutton(radio_frame, text="Statistics by type", variable=self.box, value="Statistics by type", command=self.refresh_results_window) msgCat = Radiobutton(radio_frame, text="Messages by category", variable=self.box, value="Messages by category", command=self.refresh_results_window) msg = Radiobutton(radio_frame, text="Messages", variable=self.box, value="Messages", command=self.refresh_results_window) sourceFile = Radiobutton(radio_frame, text="Source File", variable=self.box, value="Source File", command=self.refresh_results_window) report.select() report.grid(column=0, row=0, sticky=W) rawMet.grid(column=1, row=0, sticky=W) dup.grid(column=2, row=0, sticky=W) msg.grid(column=3, row=0, sticky=W) stat.grid(column=0, row=1, sticky=W) msgCat.grid(column=1, row=1, sticky=W) ext.grid(column=2, row=1, sticky=W) sourceFile.grid(column=3, row=1, sticky=W) #dictionary for check boxes and associated error term self.msg_type_dict = { 'I' : lambda : self.information_box.get() == 1, 'C' : lambda : self.convention_box.get() == 1, 'R' : lambda : self.refactor_box.get() == 1, 'E' : lambda : self.error_box.get() == 1, 'W' : lambda : self.warning_box.get() == 1, 'F' : lambda : self.fatal_box.get() == 1 } self.txtModule.focus_set() def select_recent_file(self, event): """adds the selected file in the history listbox to the Module box""" if not self.showhistory.size(): return selected = self.showhistory.curselection() item = self.showhistory.get(selected) #update module self.txtModule.delete(0, END) self.txtModule.insert(0, item) def refresh_msg_window(self): """refresh the message window with current output""" #clear the window self.lbMessages.delete(0, END) self.visible_msgs = [] for msg in self.msgs: if (self.msg_type_dict.get(msg.C)()): self.visible_msgs.append(msg) msg_str = convert_to_string(msg) self.lbMessages.insert(END, msg_str) fg_color = COLORS.get(msg_str[:3], 'black') self.lbMessages.itemconfigure(END, fg=fg_color) def refresh_results_window(self): """refresh the results window with current output""" #clear the window self.results.delete(0, END) try: for res in self.tabs[self.box.get()]: self.results.insert(END, res) except: pass def process_incoming(self): """process the incoming messages from running pylint""" while self.msg_queue.qsize(): try: msg = self.msg_queue.get(0) if msg == "DONE": self.report_stream.output_contents() return False #adding message to list of msgs self.msgs.append(msg) #displaying msg if message type is selected in check box if (self.msg_type_dict.get(msg.C)()): self.visible_msgs.append(msg) msg_str = convert_to_string(msg) self.lbMessages.insert(END, msg_str) fg_color = COLORS.get(msg_str[:3], 'black') self.lbMessages.itemconfigure(END, fg=fg_color) except Queue.Empty: pass return True def periodic_call(self): """determine when to unlock the run button""" if self.process_incoming(): self.root.after(100, self.periodic_call) else: #enabling button so it can be run again self.btnRun.config(state=NORMAL) def mainloop(self): """launch the mainloop of the application""" self.root.mainloop() def quit(self, _=None): """quit the application""" self.root.quit() def halt(self): """program halt placeholder""" return def file_open(self, package=False, _=None): """launch a file browser""" if not package: filename = askopenfilename(parent=self.root, filetypes=[('pythonfiles', '*.py'), ('allfiles', '*')], title='Select Module') else: filename = askdirectory(title="Select A Folder", mustexist=1) if filename == (): return self.txtModule.delete(0, END) self.txtModule.insert(0, filename) def update_filenames(self): """update the list of recent filenames""" filename = self.txtModule.get() if not filename: filename = os.getcwd() if filename+'\n' in self.filenames: index = self.filenames.index(filename+'\n') self.filenames.pop(index) #ensure only 10 most recent are stored if len(self.filenames) == 10: self.filenames.pop() self.filenames.insert(0, filename+'\n') def set_history_window(self): """update the history window with info from the history file""" #clear the window self.showhistory.delete(0, END) # keep the last 10 most recent files try: view_history = open(HOME+HISTORY, 'r') for hist in view_history.readlines(): if not hist in self.filenames: self.filenames.append(hist) self.showhistory.insert(END, hist.split('\n')[0]) view_history.close() except IOError: # do nothing since history file will be created later return def run_lint(self, _=None): """launches pylint""" self.update_filenames() self.root.configure(cursor='watch') self.reporter = GUIReporter(self, output=self.report_stream) module = self.txtModule.get() if not module: module = os.getcwd() #cleaning up msgs and windows self.msgs = [] self.visible_msgs = [] self.lbMessages.delete(0, END) self.tabs = {} self.results.delete(0, END) self.btnRun.config(state=DISABLED) #setting up a worker thread to run pylint worker = Thread(target=lint_thread, args=(module, self.reporter, self,)) self.periodic_call() worker.start() # Overwrite the .pylint-gui-history file with all the new recently added files # in order from filenames but only save last 10 files write_history = open(HOME+HISTORY, 'w') write_history.writelines(self.filenames) write_history.close() self.set_history_window() self.root.configure(cursor='') def show_sourcefile(self, event=None): selected = self.lbMessages.curselection() if not selected: return msg = self.visible_msgs[int(selected[0])] scroll = msg.line - 3 if scroll < 0: scroll = 0 self.tabs["Source File"] = open(msg.abspath, "r").readlines() self.box.set("Source File") self.refresh_results_window() self.results.yview(scroll) self.results.select_set(msg.line - 1) def show_messages_context(self, event): """Show the message listbox's context menu""" # Select the item that was clicked index = self.lbMessages.nearest(event.y) self.lbMessages.selection_clear(0, END); self.lbMessages.selection_set(index) self.lbMessages.activate(index) self.mnMessages.tk_popup(event.x_root, event.y_root) def add_to_ignore_patchfile(self, event=None): """ Add the selected message to the ignore patchfile. This means that this message will now be ignored by pylint-patcher. """ selected = self.lbMessages.curselection() if not selected: return selected_index = int(selected[0]) msg = self.visible_msgs[selected_index] self.differ.add_disable_pragma(msg.abspath, msg.line, msg.symbol) self.differ.diff() del self.msgs[self.msgs.index(msg)] del self.visible_msgs[selected_index] self.lbMessages.delete(selected_index)