class Tooltip(Toplevel): """ Tooltip class """ def __init__(self, parent, **kwargs): """ Create a tooltip with given parent. KEYWORD OPTIONS title, alpha, padx, pady, font, background, foreground, image, text """ Toplevel.__init__(self, parent) if 'title' in kwargs: self.title(kwargs['title']) self.transient(parent) self.attributes('-type', 'tooltip') self.attributes('-alpha', kwargs.get('alpha', 0.75)) self.overrideredirect(True) self.configure(padx=kwargs.get('padx', 4)) self.configure(pady=kwargs.get('pady', 4)) self.font = Font(self, kwargs.get('font', '')) self.style = Style(self) if 'background' in kwargs: bg = kwargs['background'] self.configure(background=bg) self.style.configure('tooltip.TLabel', background=bg) if 'foreground' in kwargs: self.style.configure('tooltip.TLabel', foreground=kwargs['foreground']) self.im = kwargs.get('image', None) self.label = Label(self, text=kwargs.get('text', ''), image=self.im, style='tooltip.TLabel', font=self.font, wraplength='6c', compound=kwargs.get('compound', 'left')) self.label.pack() def configure(self, **kwargs): if 'text' in kwargs: self.label.configure(text=kwargs.pop('text')) if 'image' in kwargs: self.label.configure(image=kwargs.pop('image')) if 'background' in kwargs: self.style.configure('tooltip.TLabel', background=kwargs['background']) if 'foreground' in kwargs: fg = kwargs.pop('foreground') self.style.configure('tooltip.TLabel', foreground=fg) if 'alpha' in kwargs: self.attributes('-alpha', kwargs.pop('alpha')) if 'font' in kwargs: font = Font(self, kwargs.pop('font')) self.font.configure(**font.actual()) Toplevel.configure(self, **kwargs) def config(self, **kw): self.configurere(**kw) def cget(self, key): if key in ['text', 'image']: return self.label.cget(key) elif key == 'font': return self.font elif key == 'alpha': return self.attributes('-alpha') elif key in ['foreground', 'background']: return self.style.lookup('tooltip.TLabel', key) else: return Toplevel.cget(self, key)
class ParamsFrame(Notebook): ''' This class implements various operations with parameters like loading and saving from and to file, modifying parameters values. Attributes: parent (Tk object): parent of this widget. params (Parameters): Parameters object with values of all parameters. current_categories (list of str): list of current valid categories. input_categories (CategoriesCheckBox): frame for displaying input categories. output_categories (CategoriesCheckBox): frame for displaying output categories. params_from_file_lbl (Label): label for displaying file name if parameters were loaded from file. data_from_params_file(StringVar): StringVar object used for communication of this widget with DataFrame. Changing the value of data_from_params_file triggers changes in DataFrame (like clearing all data and loading data from file). str_var_for_input_output_boxes (ObserverStringVar): ObserverStringVar object used for storing input and output categories and for tracking changes in input and output categories. weight_tab (WeightFrame): widget used for displaying and editing weights. load_without_data (IntVar): IntVar object used for Checkbutton, if its value is 1, then parameters will be loaded from file without data, if its value is 0, then parameters will be loaded from file with data. options_frame (OptionsFrame): widget used for displaying and modifying some of the parameters. Args: parent (Tk object): parent of this widget. current_categories (list of str): list of current valid categories. data_from_params_file(StringVar): StringVar object used for communication of this widget with DataFrame. Changing the value of data_from_params_file triggers changes in DataFrame (like clearing all data and loading data from file). str_var_for_input_output_boxes (ObserverStringVar): ObserverStringVar object used for storing input and output categories and for tracking changes in input and output categories. weights_status_str (StringVar): StringVar object used for changing label of weights editor, for details see WeightFrame. ''' def __init__(self, parent, current_categories, data_from_params_file, str_var_for_input_output_boxes, weights_status_str, *args, **kw): Notebook.__init__(self, parent, *args, **kw) self.parent = parent self.params = Parameters() self.current_categories = current_categories self.input_categories_frame = None self.output_categories_frame = None self.params_from_file_lbl = None self.data_from_params_file = data_from_params_file self.str_var_for_input_output_boxes = str_var_for_input_output_boxes self.weight_tab = None self.load_without_data = IntVar() self.options_frame = None self.create_widgets(weights_status_str) def create_widgets(self, weights_status_str): ''' Creates all widgets. ''' self.enable_traversal() self._create_params_tab() self.weight_tab = WeightFrame(self, self.current_categories, self.params, weights_status_str) self.add(self.weight_tab, text='Weights editor') def change_weight_tab_name(self, new_name): ''' Changes name of weights editor tab. Args: new_name (str): new name for weights editor tab. ''' self.tab(1, text=new_name) def _create_params_tab(self): ''' Creates all widgets of the parameters tab. ''' frame_for_all_objects = VerticalScrolledFrame(self) frame_for_all_objects.columnconfigure(0, weight=1) frame_for_all_objects.rowconfigure(0, weight=1) params_tab = frame_for_all_objects.interior params_tab.columnconfigure(0, weight=1, pad=5) frame_for_save_btns = Frame(params_tab) frame_for_save_btns.columnconfigure(0, weight=1) frame_for_save_btns.columnconfigure(1, weight=1) load_btn = Button(frame_for_save_btns, text='Load parameters', command=self.load_file) load_btn.grid(row=0, column=0, sticky=W + N, pady=2) load_wo_data_box = Checkbutton(frame_for_save_btns, text='Load without data', variable=self.load_without_data) load_wo_data_box.grid(row=1, column=0, sticky=W + N, pady=2) save_btn = Button(frame_for_save_btns, text='Save parameters', command=self.on_save_params) save_btn.grid(row=0, column=1, sticky=E + N, pady=2) save_btn = Button(frame_for_save_btns, text='Save parameters as...', command=self.on_save_params_as) save_btn.grid(row=1, column=1, sticky=E + N, pady=2) frame_for_save_btns.grid(row=0, column=0, sticky=E + W, padx=XPAD_VALUE, pady=YPAD_VALUE) self.params_from_file_lbl = Label(params_tab, text=TEXT_FOR_PARAMS_LBL, anchor=W, justify=LEFT, wraplength=MAX_FILE_LBL_LENGTH) self.params_from_file_lbl.grid(row=1, column=0, columnspan=3, sticky=W + N, padx=XPAD_VALUE, pady=YPAD_VALUE) input_categories_list = CategoriesCheckBox(params_tab, 'Input categories:', True, self.params, 'INPUT_CATEGORIES') self.input_categories_frame = input_categories_list input_categories_list.grid(row=4, column=0, sticky=W + N + S + E, padx=XPAD_VALUE, pady=YPAD_VALUE, columnspan=2) output_categories_list = CategoriesCheckBox(params_tab, 'Output categories:', False, self.params, 'OUTPUT_CATEGORIES') self.output_categories_frame = output_categories_list output_categories_list.grid(row=5, column=0, sticky=W + N + S + E, padx=XPAD_VALUE, pady=YPAD_VALUE, columnspan=2) self.options_frame = OptionsFrame(params_tab, self.params, self.current_categories, self.input_categories_frame, self.output_categories_frame) self.options_frame.grid(row=6, column=0, columnspan=2, sticky=N + S + W + E, padx=XPAD_VALUE, pady=YPAD_VALUE) self.add(frame_for_all_objects, text='Parameters') def on_save_params(self): ''' Saves current parameter values to a file from where the\ parameters were loaded. This file name is displayed. If no file name is displayed (i.e. parameters were not previously loaded from file), then asksaveasfilename dialogue is called. ''' file_name = self.params_from_file_lbl.cget('text') if TEXT_FOR_PARAMS_LBL in file_name: file_name = file_name[len(TEXT_FOR_PARAMS_LBL):] if file_name: write_parameters_to_file(self.params, file_name) else: self.on_save_params_as() def on_save_params_as(self): ''' Calls asksaveasfilename dialogue and saves current values of parameters to the specified file. ''' file_name = self._get_file_name_to_save() if file_name: write_parameters_to_file(self.params, file_name) def _get_file_name_to_save(self): ''' Calls asksaveasfilename dialogue. This method is overridden in unit tests. Returns: (str): file name. ''' return asksaveasfilename(filetypes=[('Text files', '*.txt')], defaultextension='.txt') def load_file(self): ''' Loads parameters from file specified by the user. ''' file_name = self._get_filename_for_load() if file_name: self.str_var_for_input_output_boxes.input_categories.clear() self.str_var_for_input_output_boxes.output_categories.clear() # save previous params params_to_restore = dict() for param_name in CATEGORICAL_AND_DATA_FIELDS: params_to_restore[ param_name] = self.params.get_parameter_value(param_name) self.params.copy_all_params(parse_parameters_from_file(file_name)) if self.load_without_data.get() == 0: self.load_data_file_and_related_params(file_name, params_to_restore) else: self.data_from_params_file.set('') # restore previous parameters for param_name, value in params_to_restore.items(): self.params.update_parameter(param_name, value) self.options_frame.set_params_values() def _get_filename_for_load(self): ''' Calls askopenfilename dialogue. This method is overridden in unit tests. Returns: (str): file name. ''' file_types = [('Text files', '*.txt'), ('All files', '*.*')] file_name = askopenfilename(title='Choose a file', filetypes=file_types) return file_name def load_data_file_and_related_params(self, file_name, params_to_restore): ''' Loads data if possible and sets widgets to proper values depending on parameters. Args: file_name (str): file name of file with parameters. It is needed to display it on parameters frame. params_to_restore (dict of str to str): dictionary of previous values of parameters. They are used in order to restore previous values if loading of data from file fails. ''' data_file = self.params.get_parameter_value('DATA_FILE') norm_data_path = os.path.normpath(data_file) if os.path.isfile(norm_data_path): params_to_restore = dict() # I have to store this here, because when I clean all data # from data tab, it deletes these values from params for param_name in CATEGORICAL_AND_DATA_FIELDS: params_to_restore[ param_name] = self.params.get_parameter_value(param_name) # this line calls clear all from data_tab self.data_from_params_file.set(norm_data_path) self.params_from_file_lbl.config(text=TEXT_FOR_PARAMS_LBL + file_name) for param_name, value in params_to_restore.items(): self.params.update_parameter(param_name, value) self.add_categories( 'INPUT_CATEGORIES', self.input_categories_frame, self.str_var_for_input_output_boxes.input_categories) self.add_categories( 'OUTPUT_CATEGORIES', self.output_categories_frame, self.str_var_for_input_output_boxes.output_categories) self.str_var_for_input_output_boxes.set('notify') self.weight_tab.add_weights() else: self._show_warning(norm_data_path) for param_name, value in params_to_restore.items(): self.params.update_parameter(param_name, value) def _show_warning(self, norm_data_path): ''' Shows warning that data cannot be loaded from file. This method is overridden in unit tests. ''' showwarning( 'Warning', 'Cannot load data file: ' + norm_data_path + '. Parameters will be loaded without data.') def change_category_name(self, old_name, new_name): ''' Changes category name in parameters and all widgets to a new name. If new name is empty string, then some of the parameters might be lost (for example, weight restrictions will be lost). Args: old_name (str): old name of the category. new_name (str): new name of the category. ''' if old_name != new_name: self.input_categories_frame.change_category_name( old_name, new_name) self.output_categories_frame.change_category_name( old_name, new_name) self.weight_tab.add_weights() if self.options_frame.combobox_text_var.get() == old_name: self.options_frame.change_categorical_box() self.options_frame.set_categorical_box(new_name) def add_categories(self, name, frame, categories_container): ''' Adds input or output categories to a specified widget with categories from parameters. Args: name (str): name of the parameter where categories come from, possible values INPUT_CATEGORIES, OUTPUT_CATEGORIES. frame (CategoriesCheckBox): widget where categories will be added. categories_container (list of str): list of categories where categories from parameters will be added. ''' categories = self.params.get_set_of_parameters(name) for category in categories: # we add only categories that are # present in data file if category in self.current_categories: frame.add_category(category) categories_container.append(category) else: self.params.remove_category_from_params(name, category) def clear_all(self): ''' Clears all parameters and corresponding widgets. ''' self.input_categories_frame.remove_all_categories() self.output_categories_frame.remove_all_categories() self.options_frame.combobox_text_var.set('') self.weight_tab.remove_all_weights() self.params.clear_all_categorical_and_data_fields() self.params_from_file_lbl.config(text='') self.str_var_for_input_output_boxes.input_categories.clear() self.str_var_for_input_output_boxes.output_categories.clear()
def define_login_window(self): """ Define login screen with all visual elements (Fields, Buttons, ...) """ self.login_window.title("Login") # Create the label Label(self.login_window, text="Please enter login details:").grid(row=0, column=0, columnspan=2, sticky="ew", padx=10, pady=10) # USERNAME ENTRY Label(self.login_window, text="Username").grid(row=1, column=0, sticky="e", padx=10) Entry(self.login_window, textvariable=self.username_login).grid(row=1, column=1, sticky="e", padx=10) # PASSWORD ENTRY Label(self.login_window, text="Password").grid(row=2, column=0, sticky="e", padx=10) Entry(self.login_window, textvariable=self.password_login, show='*').grid(row=2, column=1, sticky="e", padx=10) # LOGIN button Button(self.login_window, text="Login", width=10, height=1, command=self.login_handler).grid(row=3, column=1, sticky="ew", padx=10, pady=5) # NEW ACCOUNT Label new_account_label = Label(self.login_window, text="No account yet? Sign up!") new_account_label.grid(row=4, column=0, columnspan=2, sticky="ew", padx=10, pady=10) f = font.Font(new_account_label, new_account_label.cget("font")) f.configure(underline=True) new_account_label.configure(font=f) new_account_label.bind("<Button-1>", self.generate_signup_window) # This function self.close_login_window, # is called when pushing the cross button right top to close the window, self.login_window.protocol("WM_DELETE_WINDOW", self.close_login_window)
class CodeStructure(BaseWidget): def __init__(self, master, manager): BaseWidget.__init__(self, master, 'Code structure', style='border.TFrame') self.rowconfigure(1, weight=1) self.columnconfigure(0, weight=1) self._manager = manager header = Frame(self) header.columnconfigure(0, weight=1) self._close_btn = Button(header, style='close.TButton', padding=0, command=lambda: self.visible.set(False)) self._close_btn.grid(row=0, column=1) self.filename = Label(header, padding=(4, 0)) self.filename.grid(row=0, column=0, sticky='w') self.codetree = CodeTree(self) self._sx = AutoHideScrollbar(self, orient='horizontal', command=self.codetree.xview) self._sy = AutoHideScrollbar(self, orient='vertical', command=self.codetree.yview) self.goto_frame = Frame(self) Label(self.goto_frame, text='Go to:').pack(side='left') self.goto_entry = AutoCompleteCombobox2(self.goto_frame, completevalues=[]) self.goto_entry.pack(side='left', fill='x', expand=True, pady=4, padx=4) self._goto_index = 0 self.codetree.configure(xscrollcommand=self._sx.set, yscrollcommand=self._sy.set) header.grid(row=0, columnspan=2, sticky='we') self.codetree.grid(row=1, column=0, sticky='ewns') self._sx.grid(row=2, column=0, sticky='ew') self._sy.grid(row=1, column=1, sticky='ns') Frame(self, style='separator.TFrame', height=1).grid(row=3, column=0, columnspan=2, sticky='ew') self.goto_frame.grid(row=4, column=0, columnspan=2, sticky='nsew') self.set_callback = self.codetree.set_callback self.goto_entry.bind('<Return>', self.goto) self.goto_entry.bind('<<ComboboxSelected>>', self.goto) self.goto_entry.bind('<Key>', self._reset_goto) @property def manager(self): return self._manager @manager.setter def manager(self, new_manager): if CONFIG.get("General", "layout") in ["vertical", "horizontal2"]: self.configure(style='TFrame') self._close_btn.grid_remove() else: self.configure(style='border.TFrame') self._close_btn.grid() if self.visible.get(): try: self._manager.forget(self) except TclError: pass self._manager = new_manager self.show() else: self._manager = new_manager def _reset_goto(self, event): self._goto_index = 0 def focus_set(self): self.goto_entry.focus_set() def hide(self): try: layout = CONFIG.get("General", "layout") if layout in ["vertical", "horizontal2"]: self.manager.hide(self) else: # save layout old = CONFIG.get("Layout", layout).split() w = self.master.winfo_width() pos = '%.3f' % (self.master.sashpos(0)/w) CONFIG.set("Layout", layout, f"{pos} {old[1]}") CONFIG.save() self.manager.forget(self) except TclError: pass def show(self): layout = CONFIG.get("General", "layout") if layout in ["vertical", "horizontal2"]: self.manager.add(self, text=self.name) self.manager.select(self) else: self.manager.insert(0, self, weight=1) w = self.master.winfo_width() pos = int(float(CONFIG.get("Layout", layout).split()[0]) * w) self.master.sashpos(0, pos) def _visibility_trace(self, *args): visible = self.visible.get() if visible: self.show() else: self.hide() CONFIG.set('Code structure', 'visible', str(visible)) CONFIG.save() def get_cells(self): return self.codetree.cells def clear(self, event=None): self.codetree.delete(*self.codetree.get_children()) self.filename.configure(text='') def populate(self, title, text): reset = self.filename.cget('text') != title self.filename.configure(text=title) self._sx.timer = self._sx.threshold + 1 self._sy.timer = self._sy.threshold + 1 try: names = list(self.codetree.populate(text, reset)) except TclError: logging.exception('CodeStructure Error') self.codetree.delete(*self.codetree.get_children()) return names.sort() self.goto_entry.delete(0, "end") self.goto_entry.set_completion_list(names) self.event_generate('<<Populate>>') def goto(self, event): name = self.goto_entry.get() res = self.codetree.tag_has(name) if res: if self._goto_index >= len(res): self._goto_index = 0 self.codetree.see(res[self._goto_index]) self.codetree.selection_remove(*self.codetree.selection()) self.codetree.selection_set(res[self._goto_index]) self._goto_index += 1
class exogene(object): def __init__(self, root): self.root = root self.root.bind("<Return>", func=self.make_apes) self.name = None self.species = None self.batch = None self.cdna = None self.splice = None self.splice_values = (("cds", "Longest ORF"), ("cdna", "Longest cDNA"), ("all", "All isoforms")) self.gdna = None self.flank = None self.peptide = None self.do_open = None self.save_path = None self.primer_path = None self.program_path = None self.message = None self.error_message = [] self.default_save_path = None self.default_organism = None self.default_primer_path = None self.default_program_path = None self.app_data = plp.home() / ".ExoGene" self.width = 35 self.program_locations = [] self.style = Style() self.style.configure("N.TLabel", foreground="black") self.style.configure("E.TLabel", foreground="red") for i in ["TButton", "TRadiobutton", "TCombobox", "TLabel", "TEntry"]: self.style.configure("D.{}".format(i), foreground="gray", background="light gray") self.style.configure("N.{}".format(i), foreground="black", background="white") if platform == "darwin": self.app_data = plp.home() / "Library/Preferences/ExoGene" self.width = 43 self.program_locations = [plp("/Applications/ApE.app")] elif platform == "win32": self.app_data = plp.home() / "AppData/Roaming/ExoGene" prog_files = plp("/Program Files") self.program_locations = [ prog_files / "ApE.exe", prog_files / "ApE_win_current.exe" ] if not self.app_data.exists(): self.app_data.mkdir() self.default_file = self.app_data / "defaults.txt" def quit(self): try: self.save_default() except Exception as e: print(e) self.root.destroy() def start(self): self.open_default() self.make_program(self.root).pack(fill="both") self.root.update() def make_program(self, root): self.gb_f = Frame(root) self.make_name_frame(self.gb_f).grid(row=1, column=1, sticky="NEWS", columnspan=2) self.make_cdna_frame(self.gb_f).grid(row=2, column=1, sticky="NSEW", rowspan=2) self.make_gdna_frame(self.gb_f).grid(row=2, column=2, sticky="NSEW") self.make_peptide_frame(self.gb_f).grid(row=3, column=2, sticky="NSEW") self.make_feature_frame(self.gb_f).grid(row=4, column=1, sticky="NSEW", columnspan=2) open_frame = Frame(self.gb_f, borderwidth=1, relief="sunken") self.make_save_frame(open_frame).grid(row=5, column=1, sticky="NSEW", columnspan=2, pady=12) self.make_auto_open(open_frame).grid(row=6, column=1, sticky="NSEW", columnspan=2) open_frame.grid(row=5, column=1, rowspan=2, columnspan=2, sticky="NWSE") self.make_message_frame(self.gb_f).grid(row=7, column=1, sticky="NSWE", ipady=12, columnspan=2) self.make_controls(self.gb_f).grid(row=8, column=1, sticky="E", columnspan=2) Frame(self.gb_f).grid(row=9, column=1) return self.gb_f def make_name_frame(self, parent): name_f = Frame(parent, borderwidth=1, relief="sunken") self.gene_label = Label(name_f, text="Gene name:") self.gene_label.grid(row=1, column=1, columnspan=2, sticky="NSW") if platform == "darwin": self.name = Text(name_f, width=25, height=1, highlightcolor="#92beeb", highlightthickness=3, font="arial") elif platform == "win32": self.name = Text(name_f, width=25, height=1) self.name.grid(row=1, column=3, columnspan=3, sticky="NSW") Label(name_f, text="Species:").grid(row=1, column=6, sticky="W") if not self.default_organism: self.default_organism = "danio rerio" self.species = StringVar(name_f, self.default_organism) Combobox(name_f, values=["danio rerio", "h**o sapiens", "mus musculus"], textvariable=self.species, width=15).grid(row=1, column=7, sticky="NSW") self.batch = IntVar(name_f) Checkbutton(name_f,variable=self.batch,text=\ "Batch input genes (comma separated)",command=self.check_batch)\ .grid(row=2,column=6,columnspan=4,sticky="NSW") self.name.bind("<Return>", func=self.enter_event) self.name.bind("<Tab>", func=self.focus_next) return name_f def make_cdna_frame(self, parent): cdna_f = Frame(parent, borderwidth=1, relief="sunken") self.cdna = IntVar(cdna_f, value=1) cb = Checkbutton(cdna_f, variable=self.cdna, text="get cDNA sequence") cb.bind("<Button-1>", self.check_box_callback) cb.grid(row=1, column=1, sticky="W") self.splice = StringVar(cdna_f) [Radiobutton(cdna_f,text=label[1],variable=self.splice,value=label[0])\ .grid(column=1,row=num+2,sticky="W") for num,label in \ enumerate(self.splice_values)] self.splice.set("cds") self.check_box_callback(cb) return cdna_f def make_gdna_frame(self, parent): gdna_f = Frame(parent, borderwidth=1, relief="sunken") self.gdna = IntVar(gdna_f, value=1) cb = Checkbutton(gdna_f, variable=self.gdna, text="get gDNA sequence") cb.bind("<Button-1>", self.check_box_callback) cb.grid(row=1, column=1, sticky="W", columnspan=2) self.flank = IntVar(gdna_f, value=0) Entry(gdna_f, width=6, text=self.flank, justify="right").grid(row=2, column=1, sticky="W") Label(gdna_f, text="bp flanking gene").grid(row=2, column=2, sticky="W") self.check_box_callback(cb) return gdna_f def make_peptide_frame(self, parent): pep_f = Frame(parent, borderwidth=1, relief="sunken") self.peptide = IntVar(pep_f, value=0) Checkbutton(pep_f,variable=self.peptide,text="get protein as FASTA")\ .grid(row=1,column=1,sticky="NSW") return pep_f def make_feature_frame(self, parent): primer_f = Frame(parent, borderwidth=1, relief="sunken") self.get_primers = IntVar(primer_f, 0) cb = Checkbutton(primer_f, text="Import primers from file", variable=self.get_primers) cb.bind("<Button-1>", self.check_box_callback) cb.grid(row=1, column=1, sticky="SNW", columnspan=3) if self.default_primer_path and self.default_primer_path.exists(): self.primer_path = self.default_primer_path else: self.primer_path = plp().home().resolve() primer_path_view = StringVar(primer_f, "") self.text_view_limit(self.primer_path, primer_path_view, width=self.width) Label(primer_f, text="Select primer file:", justify="left").grid(row=2, column=1, sticky="SNEW", columnspan=2) kw = { "method": tfd.askopenfilename, "path_var": "primer_path", "width": self.width, "title": "Select primer file", "str_var": primer_path_view } Button(primer_f, textvariable=primer_path_view, command=self.path_callback(**kw), width=35, style="B.TButton").grid(row=2, column=3, sticky="SNEW", columnspan=3) Label(primer_f, text="Allow up to", justify="left").grid(row=3, column=1, sticky="NWS") self.mismatch = IntVar(primer_f, "0") Entry(primer_f, textvariable=self.mismatch, justify="center", width=3).grid(row=3, column=2, sticky="NWS") Label(primer_f, text="mismatches in primers", justify="left").grid(row=3, column=3, sticky="NWS") self.check_box_callback(cb) return primer_f def make_save_frame(self, parent): save_f = Frame(parent) if self.default_save_path and self.default_save_path.exists(): self.save_path = self.default_save_path else: self.save_path = plp.home() save_path_view = StringVar(save_f, "") self.text_view_limit(self.save_path, save_path_view, width=self.width) Label(save_f, text="Select destination folder:", justify="left").grid(row=1, column=1, sticky="SNW", columnspan=2) kw = { "method": tfd.askdirectory, "path_var": "save_path", "title": "Destination folder for files", "str_var": save_path_view, "width": self.width } Button(save_f, textvariable=save_path_view, command=self.path_callback(**kw), width=35, style="B.TButton").grid(row=1, column=3, sticky="NSW", columnspan=3) return save_f def make_auto_open(self, parent): auto_f = Frame(parent) self.do_open = IntVar(auto_f, value=1) cb = Checkbutton( auto_f, variable=self.do_open, text="Automatically open files after annotation complete") cb.bind("<Button-1>", self.check_box_callback) cb.grid(row=1, column=1, sticky="NW", columnspan=3) if self.default_program_path and self.default_program_path.exists(): self.program_path = self.default_program_path else: self.program_path = plp.home() for i in self.program_locations: if i.exists(): self.program_path = i program_path_view = StringVar(auto_f, "") self.text_view_limit(self.program_path, program_path_view, width=self.width) Label(auto_f, text="Select ApE program:", justify="left").grid(row=2, column=1, sticky="SNW", columnspan=2) kw = { "method": tfd.askopenfilename, "path_var": "program_path", "title": "Select ApE program", "str_var": program_path_view, "width": self.width } Button(auto_f, textvariable=program_path_view, command=self.path_callback(**kw), width=35, style="B.TButton").grid(row=2, column=3, sticky="NSW", columnspan=3) self.check_box_callback(cb) return auto_f def make_message_frame(self, parent): message_f = Frame(parent) self.message = Label(message_f, text="", justify="left", style="N.TLabel") self.message.grid(row=1, column=1, sticky="NSEW") return message_f def make_controls(self, parent): control_f = Frame(parent) Button(control_f, text="OK", command=self.make_apes).grid(row=1, column=5, sticky="WE") Button(control_f, text="Reset", command=self.reset).grid(row=1, column=6, sticky="WE") Button(control_f, text="Cancel", command=self.quit).grid(row=1, column=7, sticky="WE") self.controls = control_f.winfo_children() return control_f def reset(self): self.error_message = [] self.gb_f.destroy() self.make_program(self.root).pack() self.root.update() def check_box_callback(self, event): def widget_flip(widget, state, style): if widget.winfo_class() != "TCheckbutton": widget.configure(state=state, style=style.format(widget.winfo_class())) try: widget = event.widget var = int(self.root.getvar(name=widget["variable"])) except AttributeError: widget = event var = not int(self.root.getvar(name=widget["variable"])) parent_children = self.root._nametowidget( widget.winfo_parent()).winfo_children() state = ["normal", "disabled"][var] style = ["N.{}", "D.{}"][var] [widget_flip(i, state, style) for i in parent_children] self.root.setvar(widget["variable"], not var) self.root.update() return "break" def check_primers(self): state = ["disabled", "normal"][self.get_primers.get()] [i.configure(state=state) for i in self.primer_children] self.root.update() def path_callback(self, method, path_var, str_var, width, title=""): def callback(): initial_dir = getattr(self, path_var) if initial_dir.suffix.lower() == ".app": initial_dir = initial_dir.parents[0] path = self.ask_path(method, initial_dir, title) if path: path = plp(path) self.text_view_limit(path, str_var, width) setattr(self, path_var, path) return callback def ask_path(self, method, initial_dir, title=""): path = method(initialdir=initial_dir, title=title) self.root.update() return path def text_view_limit(self, path, path_var, width=25): text = str(path) if len(text) > width: text = "..." + text[-width:] path_var.set(text) self.root.update() def enter_event(self, event): self.make_apes() return "break" def focus_next(self, event): event.widget.tk_focusNext().focus() return ("break") def check_batch(self): val = [1, 2][self.batch.get()] self.name.configure(height=val) self.name.grid(rowspan=val) self.gene_label.grid(rowspan=val) def parse_name(self): text = re_split("[\r\t\n,;]", self.name.get("1.0", "end")) return [i.strip() for i in text if i] def get_variables(self): self.vars_dict = { "species": self.species.get().strip().replace(" ", "_"), "cDNA": self.cdna.get(), "gDNA": self.gdna.get(), "flank": self.flank.get(), "save_path": self.save_path, "do_open": self.do_open.get(), "peptide": self.peptide.get(), "splice": self.splice.get(), "names": self.parse_name(), "ape_program": str(self.program_path) } return self.vars_dict.copy() def get_primer_vars(self): self.primer_dict = { "primer_path": self.primer_path, "mismatch": self.mismatch.get() } return self.primer_dict.copy() def open_default(self): if self.default_file.exists(): with open(self.default_file, 'r') as f: for line in f: try: key, value = line.split("\t") setattr(self, key.strip(), value.strip()) except Exception as e: print(e) for i in ("default_save_path", "default_primer_path", "default_program_path"): attr = getattr(self, i) if attr and plp(attr).exists(): setattr(self, i, plp(attr)) else: setattr(self, i, None) def save_default(self): temp = "{}\t{}\n".format with self.default_file.open('w') as out: out.write(temp("default_organism", self.default_organism)) for i in ("default_save_path", "default_primer_path", "default_program_path"): try: path = str(getattr(self, i).resolve()) out.write(temp(i, path)) except (AttributeError, TypeError): pass def update_default(self): if self.save_path and self.save_path.exists(): self.default_save_path = self.save_path self.default_organism = self.species.get() if self.get_primers and self.primer_path and self.primer_path.exists(): self.default_primer_path = self.primer_path if self.do_open.get( ) and self.program_path and self.program_path.exists(): self.default_program_path = self.program_path def make_apes(self, *args, **kwargs): self.message.config(style="N.TLabel") for button in self.controls: if button.cget("text").lower() != "cancel": button.configure(state="disabled") self.root.update() try: var = self.get_variables() names = [i.strip() for i in var.pop("names") if i] if not names: self.error_message.append("Enter gene name before submitting") try: num = var.get("flank") if not 0 <= int(num) <= 100000: self.error_message.append( "Enter only valid numbers for flank: 0-100000") except ValueError: self.error_message.append("Enter only valid numbers for flank") except ValueError: self.error_message.append("Enter valid number for flank") if self.get_primers.get(): try: primer_vars = self.get_primer_vars() mismatch = primer_vars.get("mismatch") try: anno = gene("", auto_start=False) primer_seqs = anno.get_primer_seqs( primer_vars.get("primer_path")) if not primer_seqs: raise IOError else: self.message.config( text="{} primers loaded from file".format( len(primer_seqs))) except Exception as e: print(e) self.error_message.append( "Could not read primer file:\nFor Excel files use the 1st column for primer names 2nd for sequences\nFor text files use comma separated name and sequence with each primer on a new line" ) except ValueError: self.error_message.append("Enter valid number for mismatch") if not self.error_message: self.update_default() connected = True names = names names_unprocessed = set(names) for i in names: try: if i not in names_unprocessed: print("Duplicate gene entered", i) else: self.message.config( text="Downloading and annotating {}".format(i)) a = gene(symbol=i, **var) if self.get_primers.get(): a.add_primers(primer_seqs, mismatch=mismatch) self.root.update() a.write_ape() try: names_unprocessed.remove(i) except KeyError: print("Duplicate gene entered", i) except error.HTTPError: self.error_message.append( "Gene '{}' could not be found for organism '{}'". format(i, self.species.get())) except request.URLError: self.error_message.append( "Unable to connect to Ensembl, check network settings") connected = False break except OSError as e: self.error_message.append(str(e)) except IndexError as e: self.error_message.append(str(e)) self.root.update() if connected: if var.get("do_open"): self.message.config(text="Opening files...") self.root.update() sleep(3) self.message.config(text="Annotation Complete") self.name.delete("1.0", "end-1c") self.name.insert("1.0", ", ".join(names_unprocessed)) self.name.tag_add("sel", "1.0", "end-1c") for button in self.controls: button.configure(state="normal") if self.error_message: self.message.config(style="E.TLabel") if self.message.cget("text") == "Annotation Complete": self.error_message.insert(0, "Annotation Complete") self.message.config(text="\n".join(self.error_message)) self.root.update() self.error_message = []