class InteractionFilterWidget(rb.RaccoonDefaultWidget, DebugTools.DebugObj, tk.Frame): """ provide the interaction filter widget with different types: HBa HBd HB Metal vdW Pi-stack T-stack Generic stack checkbutton pulldown entry button (remove) [x] | | [type..] | [ string ] [ x ] """ def __init__(self, parent, manager, settings={}, color=None, debug = False): #, destroy_cb): self.color = '#bbff33' self.color = color # differenciate filter types? rb.RaccoonDefaultWidget.__init__(self, parent) DebugTools.DebugObj.__init__(self, debug) tk.Frame.__init__(self, master=self.parent, relief='raised', bg=color, **self.BORDER) self.manager = manager self._widgets = [self] self.initIcons() self.destroy_cb = None # destroy_cb self.eventManager = self.manager.eventManager #self.eventManager = eventManager #self.event = RaccoonEvents.FilterSetSelection() self.settings = settings self.build() self.setfilter() # hack to have mouse wheel to work all the time for w in self._widgets: w.bind("<Button-4>", self.manager.mousescroll) w.bind("<Button-5>", self.manager.mousescroll) if hasattr(w, 'components'): for c in w.components(): w.component(c).bind("<Button-4>", self.manager.mousescroll) w.component(c).bind("<Button-5>", self.manager.mousescroll) def build(self): """ build the widgets""" entry_width = 10 # types strings self._notype = '< select type >' self.stringToType = { 'HB donor' : 'hbd', 'HB acceptor' : 'hba', 'HB any' : 'hb', 'Metal coord': 'metal', 'vdW contact': 'vdw', 'Pi-stacking': 'ppi', # XXX check this 'T-stacking' : 'tpi', # XXX check this 'Any stacking': 'pi', # XXX check this } self.typeToString = {} for k,v in self.stringToType.items(): self.typeToString[v] = k # active checkbutton self.active_var = tk.BooleanVar(value=False) self.active = tk.Checkbutton(self, variable=self.active_var, bg=self.color, command=self.trigger) #self.active.pack(side='left', anchor='w', expand=0, fill='none') # interaction type self.typeChoice = OptionMenuFix(self, menubutton_font = self.FONT, menubutton_bg = self.color, menu_font = self.FONT, menubutton_width = 15, menubutton_bd = 1, menubutton_highlightbackground = 'black', menubutton_borderwidth=1, menubutton_highlightcolor='black', menubutton_highlightthickness = 1, menubutton_height=1, command = self.trigger, # self.typeValidator, # self.trigger, items = sorted(self.stringToType.keys()) ) self.typeChoice.setvalue(self._notype) self.typeChoice.pack(side='left', anchor='w', expand=1, fill='x', padx=3, pady=1) self._widgets.append(self.typeChoice) # strings f = tk.Frame(self) # nested frames to have a decent alignment (Tk sucks(TM) ) ef = tk.Frame(f) # chain self.chain = Pmw.EntryField(ef, entry_font=self.FONT, labelpos = 'n', label_font=self.FONT, label_text='chain', hull_bg = self.color, entry_width=4, entry_justify='center', entry_bd = 1, entry_highlightbackground = 'black', entry_borderwidth=1, entry_highlightcolor='black', entry_highlightthickness = 1, ) self.chain.pack(side='left', anchor='n', expand=0, fill='x', padx=3 ) self.chain.component('entry').bind('<Return>', self.return_cb) self.chain.component('entry').bind('<Leave>', self.focusLeave_cb) self._widgets.append(self.chain) # residue self.residue = Pmw.EntryField(ef, entry_font=self.FONT, labelpos = 'n', label_font=self.FONT, label_text='residue', hull_bg = self.color, entry_width=entry_width, entry_justify='center', entry_highlightbackground = 'black', entry_borderwidth=1, entry_highlightcolor='black', entry_highlightthickness = 1, ) self.residue.pack(side='left', anchor='n', expand=1, fill='x', padx=3) self.residue.component('entry').bind('<Return>', self.return_cb) self.residue.component('entry').bind('<Leave>', self.focusLeave_cb) self._widgets.append(self.residue) # atom self.atom = Pmw.EntryField(ef, entry_font=self.FONT, labelpos = 'n', label_font=self.FONT, label_text='atom', hull_bg = self.color, entry_width=entry_width, entry_justify='center', entry_highlightbackground = 'black', entry_borderwidth=1, entry_highlightcolor='black', entry_highlightthickness = 1, ) self.atom.pack(side='left', anchor='n', expand=1, fill='x', padx=3) self.atom.component('entry').bind('<Return>', self.return_cb) self._widgets.append(self.atom) s = tk.Frame(f, height=15) s.pack(side='bottom', anchor='w', expand=1, fill='both') ef.pack(side='top', anchor='w', expand=1, fill='x', padx=3) f.pack(side='left', anchor='w', expand=1, fill='both') self._widgets.append(s) self._widgets.append(ef) self._widgets.append(f) # wanted/not wanted self.wanted = True self.wantedButton = tk.Button(self, font=self.FONTbold, image=self._ICON_filtoff, relief='flat') selmenu = [ None, ['wanted', 'normal', self.setPositive], ['not wanted', 'normal', self.setNegative], ['disable', 'normal', self.setOff], ] menu = rb.RacMenu(self.wantedButton, selmenu, placement='under') self.wantedButton.pack(side='left', anchor='w', expand=0, fill='none', padx=1, pady=1) self._widgets.append(self.wantedButton) # destroy button self.destruction = tk.Button(self, text="X", image=self._ICON_del, command=self.destroy, bg=self.color, relief='flat') #,width=22) #, **self.BORDER) self.destruction.pack(side='left', anchor='w', expand=0, fill='none') self._widgets.append(self.destruction) def initIcons(self): """ initialize the icons for the interface""" icon_path = CADD.Raccoon2.ICONPATH f = icon_path + os.sep + 'removeSmall.png' f = icon_path + os.sep + 'removeSmallbw.png' self._ICON_del = ImageTk.PhotoImage(Image.open(f)) f = icon_path + os.sep + 'filt_positive.png' f = icon_path + os.sep + 'filt_positiveLight.png' self._ICON_filtpos = ImageTk.PhotoImage(Image.open(f)) f = icon_path + os.sep + 'filt_negativeLight.png' self._ICON_filtneg = ImageTk.PhotoImage(Image.open(f)) f = icon_path + os.sep + 'filt_inactiveLight.png' self._ICON_filtoff = ImageTk.PhotoImage(Image.open(f)) def setPositive(self, event=None): """ set the interaction to be wanted""" self.wanted = True self.wantedButton.configure(image=self._ICON_filtpos, text='A') self.enable() #self.trigger() def setNegative(self, event=None): """ set the interaction to be not wanted""" self.wanted = False self.wantedButton.configure(image=self._ICON_filtneg, text='R') self.enable() #self.trigger() def setOff(self, event=None): """ turn off the filter""" self.wantedButton.configure(image=self._ICON_filtoff, text='R') current = self.active_var.get() self.active_var.set(False) # the filter was active, it has been disabled # so we need to send event for filterEngine to # be triggered if not self.active_var.get() == current: self.sendEvent() def return_cb(self, event=None): """ function called when return is pressed in the text fields """ if self.wanted == True: self.setPositive() else: self.setNegative() def focusLeave_cb(self, event=None): """ trigger status check for entries when they loose focus Important when the filter has been alread activated, then the user changes the text in the field """ if not self.isActive(): return self.entryValidator(quiet=False) def isActive(self, event=None): """ return if the filter is active""" return self.active_var.get() def disable(self, event=None): """ disable the filter""" self.active_var.set(False) def enable(self, event=None, quiet=True): """ enable the filter""" self.active_var.set(True) self.trigger(quiet=quiet) def entryValidator(self, string=None, quiet=True): """ validate the text entry field """ #if string == None: # string = self.entry.getvalue() chain = self.chain.getvalue().strip() res = self.residue.getvalue().strip() atom = self.atom.getvalue().strip() if not chain and not res and not atom: self.setOff() if not quiet: t = 'Interaction specification not valid' i = 'error' m = ('The interaction must be defined by typing at ' 'least a chain, residue or atom string.\n' 'Single character "?" and multi-character "*" wildcards can be used.' ) tmb.showinfo(parent=self, title=t, icon=i, message=m) return False return True def typeValidator(self, string=None, quiet=False): """ validate type selection """ _type = self.typeChoice.getvalue() if _type == self._notype: if not quiet: t = 'Filter type error' i = 'warning' m = ('An interaction type must be selected') tmb.showinfo(parent=self, title=t, icon=i, message=m) self.setOff() return False return True def isvalid(self, quiet=True): """return if the entry is valid""" typeCheck = self.typeValidator(quiet=quiet) if not typeCheck: # or not self.isActive(): return if not self.isActive(): return entryCheck = self.entryValidator(quiet=quiet) return (typeCheck and entryCheck) def trigger(self, event=None, quiet=False): """ command executed when the filter is activated""" #curr_state = self.isActive() # active_var.get() if not self.isvalid(quiet=quiet): return self.active_var.set(True) self.sendEvent() def sendEvent(self): """ trigger the event""" e = RaccoonEvents.FilterInteractionEvent() self.eventManager.dispatchEvent(e) def getType(self): """ return interaction type""" return self.stringToType[ self.typeChoice.getvalue() ] def getPattern(self): """ return interaction text pattern""" c = self.chain.getvalue().strip() r = self.residue.getvalue().strip() a = self.atom.getvalue().strip() if c == "": c = "*" if r == "": r = "*" if a == "": a = "*" return "%s:%s:%s" % (c,r,a) def setfilter(self, settings={}): """ initialize filter with requested values""" if settings == {}: settings == self.settings if settings == {}: return t = self.settings['type'] t = self.typeToString[t] e = self.settings['patter'] self.typeChoice.setvalue(t) self.entry.setvalue(e) if self.isvalid(): self.active_var.set(True) # FIXME set this to active? #return { 'type' : t, 'pattern' : e } def destroy(self, event=None): """ remove itself from the list of filters and unpack the widget """ t = 'Delete filter' i = 'info' m = ('Remove this filter?') if self.isvalid(): if not tmb.askyesno(parent=self, title=t, message=m, icon=i): return self.destroy_cb()
class JobSubmissionInterface(rb.RaccoonDefaultWidget): """ ask for Project, Exp, VS info...""" def __init__(self, parent, jmanager, app, suggest={}): """ parent : tkparent jmanager : job manager tree (to query current prj,exp...) app : containing app """ rb.RaccoonDefaultWidget.__init__(self, parent) self.jmanager = jmanager self.app = app self._new = '<new>' self.jobdata = None self.suggest = suggest self.initIcons() self.build() def initIcons(self): pass def close(self, result): """ close the window and decides what to do if OK requested, check values and start submission """ if result == 'OK': if not self.checkinfo(): return p = self.getPrj() e = self.getExp() t = self.tag_entry.getvalue().strip() self.jobdata = {'prj': p, 'exp': e, 'tag': t} self.win.deactivate(self.jobdata) else: self.win.deactivate(False) def getPrj(self): """ return the project name""" m = ('The project name is not valid.\n') p = self.prj_pull.getvalue() # old project if p == self._new: if not self.prj_new.valid(): self.errorMsg(m) return False p = self.prj_new.getvalue() return p def getExp(self): """ return the experiment name""" m = ('The experiment name is not valid.\n') e = self.exp_pull.getvalue() # old project if e == self._new: if not self.exp_new.valid(): self.errorMsg(m) return False e = self.exp_new.getvalue() return e def getTag(self): """ return the tag""" tag = self.tag_entry.getvalue() return tag def checkDuplicates(self): """ check that the jobs that are going to be submitted do not have the same name of already submitted jobs """ m = ('The submission cannot be performed because there ' 'are already jobs with the same name stored in project %s ' '/experiment %s.\n\n' 'Either create new project/experiments or use a different tag.') job_info = { 'prj': self.getPrj(), 'exp': self.getExp(), 'tag': self.getTag() } report = self.app.testSshJobs(job_info) e = [] if len(report['server_duplicates']): e.append('the current server') if len(report['local_duplicates']): e.append('the local client') if len(e): m = m % (job_info['prj'], job_info['exp']) self.errorMsg(m) return False return True def checkinfo(self): """ check that user provided info are valid""" if not self.getPrj(): return False if not self.getExp(): return False if not self.checkDuplicates(): return False return True def errorMsg(self, message): """ display submission entries error""" t = 'Incorrect name entry' i = 'error' tmb.showinfo(parent=self.win.interior(), title=t, message=message, icon=i) return def getinfo(self): """ return the user provided info""" return self.jobdata def _setprjname(self, event=None): choice = self.prj_pull.getvalue() if choice == self._new: self.prj_new.grid(row=4, column=2, sticky='we', padx=4, pady=4) self.prj_new.checkentry() else: self.prj_new.grid_forget() exp_list = self._getexplist() self.exp_pull.setitems(exp_list) self.exp_pull.setvalue(exp_list[-1]) self.exp_pull.invoke() def _getexplist(self, event=None): prj = self.prj_pull.getvalue() if prj == self._new: return [self._new] else: exp_list = sorted(self.info[prj].keys()) return exp_list + [self._new] def _setexpname(self, event=None): choice = self.exp_pull.getvalue() if choice == self._new: self.exp_new.grid(row=8, column=2, sticky='we', padx=4, pady=4) self.exp_new.checkentry() else: self.exp_new.grid_forget() def build(self): # get info from the current manager self.info = self.jmanager.getTreeGraph() self.prj_list = sorted(self.info.keys()) + [self._new] #self.prj_list.append(self._new) self.win = Pmw.Dialog(parent=self.parent, buttons=('OK', 'Cancel'), title='Submit jobs', command=self.close) w = self.win.interior() bbox = self.win.component('buttonbox') for i in range(bbox.numbuttons()): bbox.button(i).configure(font=self.FONT, default='disabled', **self.BORDER) tk.Label(w, text='Select the new VS properties', font=self.FONT).grid(row=0, column=1, sticky='we', columnspan=3, padx=5, pady=5) tk.Frame(w, height=2, bd=1, relief='sunken').grid(row=1, column=0, sticky='ew', columnspan=3, pady=3) # project tk.Label(w, text='Project', font=self.FONTbold, width=12, anchor='e').grid(row=3, column=1, sticky='we') tk.Label(w, text='', font=self.FONT, width=10).grid(row=4, column=1, sticky='we', pady=5) # placeholder for entry self.prj_pull = OptionMenuFix(w, menubutton_width=30, menubutton_font=self.FONT, menu_font=self.FONT, menubutton_bd=1, menubutton_highlightbackground='black', menubutton_borderwidth=1, menubutton_highlightcolor='black', menubutton_highlightthickness=1, menubutton_height=1, items=self.prj_list, initialitem=-1, command=self._setprjname) self.prj_pull.grid(row=3, column=2, sticky='we', padx=3) self.prj_new = Pmw.EntryField(w, value='', validate={ 'validator': hf.validateAscii, 'minstrict': 0 }) #, self.prj_new.component('entry').configure(justify='left', font=self.FONT, bg='pink', width=33, **self.BORDER) # -------------------------------- tk.Frame(w, height=2, bd=1, relief='sunken').grid(row=6, column=0, sticky='ew', columnspan=3, pady=3) # experiment tk.Label(w, text='Experiment', font=self.FONTbold, width=12, anchor='e').grid(row=7, column=1, sticky='we') tk.Label(w, text='', font=self.FONT, width=10).grid(row=8, column=1, sticky='we', pady=5) # placeholder for entry self.exp_pull = OptionMenuFix(w, labelpos='w', menubutton_width=30, menubutton_font=self.FONT, menu_font=self.FONT, menubutton_bd=1, menubutton_highlightbackground='black', menubutton_borderwidth=1, menubutton_highlightcolor='black', menubutton_highlightthickness=1, menubutton_height=1, items=[self._new], initialitem=-1, command=self._setexpname) self.exp_pull.grid(row=7, column=2, sticky='we', padx=3) self.exp_new = Pmw.EntryField(w, value='', validate={ 'validator': hf.validateAscii, 'minstrict': 0 }) #, self.exp_new.component('entry').configure(justify='left', font=self.FONT, bg='pink', width=30, **self.BORDER) # initialize the interface with the projects self._setprjname() self.prj_pull.setvalue(self.prj_list[-1]) # -------------------------------- tk.Frame(w, height=2, bd=1, relief='sunken').grid(row=9, column=0, sticky='ew', columnspan=3, pady=3) # job tag tk.Label(w, text='Optional jobs name tag', font=self.FONT).grid(row=10, column=1, columnspan=3, sticky='we', padx=5) self.tag_entry = Pmw.EntryField(w, value='', validate=hf.validateAsciiEmpty) #, self.tag_entry.component('entry').configure(justify='left', font=self.FONT, bg='white', width=30, **self.BORDER) self.tag_entry.grid(row=11, column=1, columnspan=3, sticky='we', padx=4, pady=4) self.win.bind('<Escape>', self.close) self.setSuggest() self.win.activate() def setSuggest(self): """ fill the submission with the suggestions""" if self.suggest == {}: return if 'prj' in self.suggest.keys(): prj = self.suggest.pop('prj') self.prj_pull.setvalue(prj) self.prj_pull.invoke() if 'exp' in self.suggest.keys(): exp = self.suggest.pop('exp') self.exp_pull.setvalue(exp) self.exp_pull.invoke() if 'tag' in self.suggest.keys(): tag = self.suggest.pop('tag') else: tag = 'RESTARTED' self.tag_entry.setvalue(tag)
class JobSubmissionInterface(rb.RaccoonDefaultWidget): """ ask for Project, Exp, VS info...""" def __init__(self, parent, jmanager, app, suggest={}): """ parent : tkparent jmanager : job manager tree (to query current prj,exp...) app : containing app """ rb.RaccoonDefaultWidget.__init__(self, parent) self.jmanager = jmanager self.app = app self._new = '<new>' self.jobdata = None self.suggest = suggest self.initIcons() self.build() def initIcons(self): pass def close(self, result): """ close the window and decides what to do if OK requested, check values and start submission """ if result == 'OK': if not self.checkinfo(): return p = self.getPrj() e = self.getExp() t = self.tag_entry.getvalue().strip() self.jobdata = {'prj' : p, 'exp': e, 'tag':t} self.win.deactivate(self.jobdata) else: self.win.deactivate(False) def getPrj(self): """ return the project name""" m = ('The project name is not valid.\n') p = self.prj_pull.getvalue() # old project if p == self._new: if not self.prj_new.valid(): self.errorMsg(m) return False p = self.prj_new.getvalue() return p def getExp(self): """ return the experiment name""" m = ('The experiment name is not valid.\n') e = self.exp_pull.getvalue() # old project if e == self._new: if not self.exp_new.valid(): self.errorMsg(m) return False e = self.exp_new.getvalue() return e def getTag(self): """ return the tag""" tag = self.tag_entry.getvalue() return tag def checkDuplicates(self): """ check that the jobs that are going to be submitted do not have the same name of already submitted jobs """ m = ('The submission cannot be performed because there ' 'are already jobs with the same name stored in project %s ' '/experiment %s.\n\n' 'Either create new project/experiments or use a different tag.') job_info = {'prj' : self.getPrj(), 'exp' : self.getExp(), 'tag' : self.getTag() } report = self.app.testSshJobs(job_info) e = [] if len(report['server_duplicates']): e.append('the current server') if len(report['local_duplicates']): e.append('the local client') if len(e): m = m % ( job_info['prj'], job_info['exp']) self.errorMsg(m) return False return True def checkinfo(self): """ check that user provided info are valid""" if not self.getPrj(): return False if not self.getExp(): return False if not self.checkDuplicates(): return False return True def errorMsg(self, message): """ display submission entries error""" t = 'Incorrect name entry' i = 'error' tmb.showinfo(parent=self.win.interior(), title=t, message=message, icon=i) return def getinfo(self): """ return the user provided info""" return self.jobdata def _setprjname(self, event=None): choice = self.prj_pull.getvalue() if choice == self._new: self.prj_new.grid(row=4, column=2, sticky='we',padx=4,pady=4) self.prj_new.checkentry() else: self.prj_new.grid_forget() exp_list = self._getexplist() self.exp_pull.setitems( exp_list ) self.exp_pull.setvalue( exp_list[-1]) self.exp_pull.invoke() def _getexplist(self, event=None): prj = self.prj_pull.getvalue() if prj == self._new: return [self._new] else: exp_list = sorted(self.info[prj].keys()) return exp_list + [self._new] def _setexpname(self, event=None): choice = self.exp_pull.getvalue() if choice == self._new: self.exp_new.grid(row=8,column=2, sticky='we', padx=4,pady=4) self.exp_new.checkentry() else: self.exp_new.grid_forget() def build(self): # get info from the current manager self.info = self.jmanager.getTreeGraph() self.prj_list = sorted(self.info.keys()) + [self._new] #self.prj_list.append(self._new) self.win = Pmw.Dialog(parent=self.parent, buttons=('OK', 'Cancel'), title = 'Submit jobs', command = self.close) w = self.win.interior() bbox = self.win.component('buttonbox') for i in range(bbox.numbuttons()): bbox.button(i).configure(font=self.FONT, default='disabled', **self.BORDER) tk.Label(w, text='Select the new VS properties', font=self.FONT).grid(row=0,column=1, sticky='we', columnspan=3,padx=5,pady=5) tk.Frame(w,height=2,bd=1,relief='sunken').grid(row=1, column=0, sticky='ew', columnspan=3, pady=3) # project tk.Label(w, text='Project', font=self.FONTbold, width=12,anchor='e').grid(row=3,column=1,sticky='we') tk.Label(w, text='', font=self.FONT, width=10).grid(row=4,column=1,sticky='we',pady=5) # placeholder for entry self.prj_pull = OptionMenuFix(w, menubutton_width=30, menubutton_font=self.FONT, menu_font=self.FONT, menubutton_bd = 1, menubutton_highlightbackground = 'black', menubutton_borderwidth=1, menubutton_highlightcolor='black', menubutton_highlightthickness = 1, menubutton_height=1, items = self.prj_list, initialitem=-1, command = self._setprjname) self.prj_pull.grid(row=3,column=2,sticky='we',padx=3) self.prj_new = Pmw.EntryField(w, value='', validate = {'validator' : hf.validateAscii, 'minstrict': 0}) #, self.prj_new.component('entry').configure(justify='left', font=self.FONT, bg='pink',width=33, **self.BORDER) # -------------------------------- tk.Frame(w,height=2,bd=1,relief='sunken').grid(row=6, column=0, sticky='ew', columnspan=3, pady=3) # experiment tk.Label(w, text='Experiment', font=self.FONTbold, width=12,anchor='e').grid(row=7,column=1,sticky='we') tk.Label(w, text='', font=self.FONT, width=10).grid(row=8,column=1,sticky='we',pady=5) # placeholder for entry self.exp_pull = OptionMenuFix(w,labelpos='w', menubutton_width=30, menubutton_font=self.FONT, menu_font=self.FONT, menubutton_bd = 1, menubutton_highlightbackground = 'black', menubutton_borderwidth=1, menubutton_highlightcolor='black', menubutton_highlightthickness = 1, menubutton_height=1, items=[self._new], initialitem=-1, command = self._setexpname) self.exp_pull.grid(row=7, column =2, sticky='we',padx=3) self.exp_new = Pmw.EntryField(w, value='', validate = {'validator' : hf.validateAscii, 'minstrict':0}) #, self.exp_new.component('entry').configure(justify='left', font=self.FONT, bg='pink',width=30, **self.BORDER) # initialize the interface with the projects self._setprjname() self.prj_pull.setvalue( self.prj_list[-1]) # -------------------------------- tk.Frame(w,height=2,bd=1,relief='sunken').grid(row=9, column=0, sticky='ew', columnspan=3, pady=3) # job tag tk.Label(w, text='Optional jobs name tag', font=self.FONT).grid(row=10, column=1,columnspan=3,sticky='we',padx=5) self.tag_entry = Pmw.EntryField(w, value='', validate = hf.validateAsciiEmpty) #, self.tag_entry.component('entry').configure(justify='left', font=self.FONT, bg='white',width=30, **self.BORDER) self.tag_entry.grid(row=11,column=1, columnspan=3, sticky='we', padx=4,pady=4) self.win.bind('<Escape>', self.close) self.setSuggest() self.win.activate() def setSuggest(self): """ fill the submission with the suggestions""" if self.suggest == {}: return if 'prj' in self.suggest.keys(): prj = self.suggest.pop('prj') self.prj_pull.setvalue(prj) self.prj_pull.invoke() if 'exp' in self.suggest.keys(): exp = self.suggest.pop('exp') self.exp_pull.setvalue(exp) self.exp_pull.invoke() if 'tag' in self.suggest.keys(): tag = self.suggest.pop('tag') else: tag = 'RESTARTED' self.tag_entry.setvalue(tag)
class InteractionFilterWidget(rb.RaccoonDefaultWidget, DebugTools.DebugObj, tk.Frame): """ provide the interaction filter widget with different types: HBa HBd HB Metal vdW Pi-stack T-stack Generic stack checkbutton pulldown entry button (remove) [x] | | [type..] | [ string ] [ x ] """ def __init__(self, parent, manager, settings={}, color=None, debug=False): #, destroy_cb): self.color = '#bbff33' self.color = color # differenciate filter types? rb.RaccoonDefaultWidget.__init__(self, parent) DebugTools.DebugObj.__init__(self, debug) tk.Frame.__init__(self, master=self.parent, relief='raised', bg=color, **self.BORDER) self.manager = manager self._widgets = [self] self.initIcons() self.destroy_cb = None # destroy_cb self.eventManager = self.manager.eventManager #self.eventManager = eventManager #self.event = RaccoonEvents.FilterSetSelection() self.settings = settings self.build() self.setfilter() # hack to have mouse wheel to work all the time for w in self._widgets: w.bind("<Button-4>", self.manager.mousescroll) w.bind("<Button-5>", self.manager.mousescroll) if hasattr(w, 'components'): for c in w.components(): w.component(c).bind("<Button-4>", self.manager.mousescroll) w.component(c).bind("<Button-5>", self.manager.mousescroll) def build(self): """ build the widgets""" entry_width = 10 # types strings self._notype = '< select type >' self.stringToType = { 'HB donor': 'hbd', 'HB acceptor': 'hba', 'HB any': 'hb', 'Metal coord': 'metal', 'vdW contact': 'vdw', 'Pi-stacking': 'ppi', # XXX check this 'T-stacking': 'tpi', # XXX check this 'Any stacking': 'pi', # XXX check this } self.typeToString = {} for k, v in self.stringToType.items(): self.typeToString[v] = k # active checkbutton self.active_var = tk.BooleanVar(value=False) self.active = tk.Checkbutton(self, variable=self.active_var, bg=self.color, command=self.trigger) #self.active.pack(side='left', anchor='w', expand=0, fill='none') # interaction type self.typeChoice = OptionMenuFix( self, menubutton_font=self.FONT, menubutton_bg=self.color, menu_font=self.FONT, menubutton_width=15, menubutton_bd=1, menubutton_highlightbackground='black', menubutton_borderwidth=1, menubutton_highlightcolor='black', menubutton_highlightthickness=1, menubutton_height=1, command=self.trigger, # self.typeValidator, # self.trigger, items=sorted(self.stringToType.keys())) self.typeChoice.setvalue(self._notype) self.typeChoice.pack(side='left', anchor='w', expand=1, fill='x', padx=3, pady=1) self._widgets.append(self.typeChoice) # strings f = tk.Frame( self) # nested frames to have a decent alignment (Tk sucks(TM) ) ef = tk.Frame(f) # chain self.chain = Pmw.EntryField( ef, entry_font=self.FONT, labelpos='n', label_font=self.FONT, label_text='chain', hull_bg=self.color, entry_width=4, entry_justify='center', entry_bd=1, entry_highlightbackground='black', entry_borderwidth=1, entry_highlightcolor='black', entry_highlightthickness=1, ) self.chain.pack(side='left', anchor='n', expand=0, fill='x', padx=3) self.chain.component('entry').bind('<Return>', self.return_cb) self.chain.component('entry').bind('<Leave>', self.focusLeave_cb) self._widgets.append(self.chain) # residue self.residue = Pmw.EntryField( ef, entry_font=self.FONT, labelpos='n', label_font=self.FONT, label_text='residue', hull_bg=self.color, entry_width=entry_width, entry_justify='center', entry_highlightbackground='black', entry_borderwidth=1, entry_highlightcolor='black', entry_highlightthickness=1, ) self.residue.pack(side='left', anchor='n', expand=1, fill='x', padx=3) self.residue.component('entry').bind('<Return>', self.return_cb) self.residue.component('entry').bind('<Leave>', self.focusLeave_cb) self._widgets.append(self.residue) # atom self.atom = Pmw.EntryField( ef, entry_font=self.FONT, labelpos='n', label_font=self.FONT, label_text='atom', hull_bg=self.color, entry_width=entry_width, entry_justify='center', entry_highlightbackground='black', entry_borderwidth=1, entry_highlightcolor='black', entry_highlightthickness=1, ) self.atom.pack(side='left', anchor='n', expand=1, fill='x', padx=3) self.atom.component('entry').bind('<Return>', self.return_cb) self._widgets.append(self.atom) s = tk.Frame(f, height=15) s.pack(side='bottom', anchor='w', expand=1, fill='both') ef.pack(side='top', anchor='w', expand=1, fill='x', padx=3) f.pack(side='left', anchor='w', expand=1, fill='both') self._widgets.append(s) self._widgets.append(ef) self._widgets.append(f) # wanted/not wanted self.wanted = True self.wantedButton = tk.Button(self, font=self.FONTbold, image=self._ICON_filtoff, relief='flat') selmenu = [ None, ['wanted', 'normal', self.setPositive], ['not wanted', 'normal', self.setNegative], ['disable', 'normal', self.setOff], ] menu = rb.RacMenu(self.wantedButton, selmenu, placement='under') self.wantedButton.pack(side='left', anchor='w', expand=0, fill='none', padx=1, pady=1) self._widgets.append(self.wantedButton) # destroy button self.destruction = tk.Button( self, text="X", image=self._ICON_del, command=self.destroy, bg=self.color, relief='flat') #,width=22) #, **self.BORDER) self.destruction.pack(side='left', anchor='w', expand=0, fill='none') self._widgets.append(self.destruction) def initIcons(self): """ initialize the icons for the interface""" icon_path = CADD.Raccoon2.ICONPATH f = icon_path + os.sep + 'removeSmall.png' f = icon_path + os.sep + 'removeSmallbw.png' self._ICON_del = ImageTk.PhotoImage(Image.open(f)) f = icon_path + os.sep + 'filt_positive.png' f = icon_path + os.sep + 'filt_positiveLight.png' self._ICON_filtpos = ImageTk.PhotoImage(Image.open(f)) f = icon_path + os.sep + 'filt_negativeLight.png' self._ICON_filtneg = ImageTk.PhotoImage(Image.open(f)) f = icon_path + os.sep + 'filt_inactiveLight.png' self._ICON_filtoff = ImageTk.PhotoImage(Image.open(f)) def setPositive(self, event=None): """ set the interaction to be wanted""" self.wanted = True self.wantedButton.configure(image=self._ICON_filtpos, text='A') self.enable() #self.trigger() def setNegative(self, event=None): """ set the interaction to be not wanted""" self.wanted = False self.wantedButton.configure(image=self._ICON_filtneg, text='R') self.enable() #self.trigger() def setOff(self, event=None): """ turn off the filter""" self.wantedButton.configure(image=self._ICON_filtoff, text='R') current = self.active_var.get() self.active_var.set(False) # the filter was active, it has been disabled # so we need to send event for filterEngine to # be triggered if not self.active_var.get() == current: self.sendEvent() def return_cb(self, event=None): """ function called when return is pressed in the text fields """ if self.wanted == True: self.setPositive() else: self.setNegative() def focusLeave_cb(self, event=None): """ trigger status check for entries when they loose focus Important when the filter has been alread activated, then the user changes the text in the field """ if not self.isActive(): return self.entryValidator(quiet=False) def isActive(self, event=None): """ return if the filter is active""" return self.active_var.get() def disable(self, event=None): """ disable the filter""" self.active_var.set(False) def enable(self, event=None, quiet=True): """ enable the filter""" self.active_var.set(True) self.trigger(quiet=quiet) def entryValidator(self, string=None, quiet=True): """ validate the text entry field """ #if string == None: # string = self.entry.getvalue() chain = self.chain.getvalue().strip() res = self.residue.getvalue().strip() atom = self.atom.getvalue().strip() if not chain and not res and not atom: self.setOff() if not quiet: t = 'Interaction specification not valid' i = 'error' m = ( 'The interaction must be defined by typing at ' 'least a chain, residue or atom string.\n' 'Single character "?" and multi-character "*" wildcards can be used.' ) tmb.showinfo(parent=self, title=t, icon=i, message=m) return False return True def typeValidator(self, string=None, quiet=False): """ validate type selection """ _type = self.typeChoice.getvalue() if _type == self._notype: if not quiet: t = 'Filter type error' i = 'warning' m = ('An interaction type must be selected') tmb.showinfo(parent=self, title=t, icon=i, message=m) self.setOff() return False return True def isvalid(self, quiet=True): """return if the entry is valid""" typeCheck = self.typeValidator(quiet=quiet) if not typeCheck: # or not self.isActive(): return if not self.isActive(): return entryCheck = self.entryValidator(quiet=quiet) return (typeCheck and entryCheck) def trigger(self, event=None, quiet=False): """ command executed when the filter is activated""" #curr_state = self.isActive() # active_var.get() if not self.isvalid(quiet=quiet): return self.active_var.set(True) self.sendEvent() def sendEvent(self): """ trigger the event""" e = RaccoonEvents.FilterInteractionEvent() self.eventManager.dispatchEvent(e) def getType(self): """ return interaction type""" return self.stringToType[self.typeChoice.getvalue()] def getPattern(self): """ return interaction text pattern""" c = self.chain.getvalue().strip() r = self.residue.getvalue().strip() a = self.atom.getvalue().strip() if c == "": c = "*" if r == "": r = "*" if a == "": a = "*" return "%s:%s:%s" % (c, r, a) def setfilter(self, settings={}): """ initialize filter with requested values""" if settings == {}: settings == self.settings if settings == {}: return t = self.settings['type'] t = self.typeToString[t] e = self.settings['patter'] self.typeChoice.setvalue(t) self.entry.setvalue(e) if self.isvalid(): self.active_var.set(True) # FIXME set this to active? #return { 'type' : t, 'pattern' : e } def destroy(self, event=None): """ remove itself from the list of filters and unpack the widget """ t = 'Delete filter' i = 'info' m = ('Remove this filter?') if self.isvalid(): if not tmb.askyesno(parent=self, title=t, message=m, icon=i): return self.destroy_cb()