modules/pmg_tk/skins/normal/__init__.py (1,000 lines of code) (raw):

from __future__ import print_function DEBUG = False import sys import re import threading import os import time if sys.version_info[0] == 2: from Tkinter import * import tkFileDialog import tkMessageBox import tkFont _next_method_name = 'next' else: from tkinter import * import tkinter.filedialog as tkFileDialog import tkinter.messagebox as tkMessageBox import tkinter.font as tkFont _next_method_name = '__next__' import Pmw from pymol.wizard import cleanup from pmg_tk.Setting import Setting from pmg_tk.SetEditor import SetEditor from pmg_tk.ColorEditor import ColorEditor from pmg_tk.skins import PMGSkin from .builder import Builder import pymol._gui import traceback root = None def encode(s): '''If `s` is unicode, attempt to encode it. On faiure, return the unicode input. Our C file I/O implementations can't handle unicode. For some file types we support reading the file in Python (supports unicode) and passing the content to the underlying C routine. ''' if sys.version_info[0] >= 3: return s if not isinstance(s, bytes): for enc in [sys.getfilesystemencoding(), 'latin1']: try: e = s.encode(enc) if os.path.exists(e): return e except UnicodeEncodeError: pass return s def split_tk_file_list(pattern): filenames = [] while True: pattern = pattern.strip() if not pattern: break sep = None if pattern[0] == '{': pattern = pattern[1:] sep = '}' a = pattern.split(sep, 1) filenames.append(a[0]) pattern = a[1] if len(a) == 2 else '' return filenames def asksaveasfilename(*args, **kwargs): filename = tkFileDialog.asksaveasfilename(*args, **kwargs) return encode(filename) def askopenfilename(*args, **kwargs): filename = tkFileDialog.askopenfilename(*args, **kwargs) if not filename: return filename multiple = kwargs.get('multiple', 0) if not multiple: filename = [filename] elif not isinstance(filename, (list, tuple)): filename = split_tk_file_list(filename) filename = map(os.path.normpath, filename) filename = map(encode, filename) filename = list(filename) if not multiple: return filename[0] return filename def _darwin_browser_open(url): os.popen("open "+url,'r').read() def darwin_browser_open(url): t = threading.Thread(target=_darwin_browser_open,args=(url,)) t.setDaemon(1) t.start() def _doAsync(self_cmd,cmmd,dirty=0): self_cmd.do(cmmd) # force strict ordering of commands if dirty: self_cmd.dirty() def _def_ext(ext): # platform-specific default extension handling if sys.platform != 'win32': ext = None # default extensions don't work right under X11/Tcl/Tk return ext class Normal(PMGSkin, pymol._gui.PyMOLDesktopGUI): pad = ' ' # extra space in menus appname = 'The PyMOL Molecular Graphics System' appversion = '0.0.0.0' # will be set in __init__ copyright = ('Copyright (C) 2003-%d\n' % (time.localtime().tm_year,) + 'Schrodinger LLC.\n'+ 'All rights reserved.') contactweb = 'http://www.pymol.org' contactemail = 'sales@schrodinger.com' # responsible for setup and takedown of the normal skin def _inc_fontsize(self, delta, font): size = font.cget('size') sign = -1 if size < 0 else 1 size = max(5, abs(size) + delta) font.configure(size=size * sign) def inc_fontsize(self, delta=1): for name in tkFont.names(): self._inc_fontsize(delta, tkFont.nametofont(name)) def inc_fontsize_dialog(self): dialog = Toplevel(self.root) grid = dialog kw = {'row': 0, 'sticky': 'w', 'padx': 5, 'pady': 5} col = getattr(iter(range(5)), _next_method_name) Button(grid, text=' - ', command=lambda: self.inc_fontsize(-1)).grid(column=col(), **kw) Button(grid, text=' + ', command=lambda: self.inc_fontsize( 1)).grid(column=col(), **kw) Label(grid, text='All GUI Font Sizes').grid(column=col(), **kw) kw['row'] = 1 col = getattr(iter(range(5)), _next_method_name) Button(grid, text=' - ', command=lambda: self._inc_fontsize(-1, self.fixedfont)).grid(column=col(), **kw) Button(grid, text=' + ', command=lambda: self._inc_fontsize( 1, self.fixedfont)).grid(column=col(), **kw) Label(grid, text='Output Font Size').grid(column=col(), **kw) dialog.title('GUI Font Size') @property def initialdir(self): ''' Be in sync with cd/pwd on the console until the first file has been browsed, then remember the last directory. ''' return self._initialdir or os.getcwd() @initialdir.setter def initialdir(self, value): self._initialdir = value def cd_dialog(self): self.cmd.cd(encode(tkFileDialog.askdirectory( title="Change Working Directory", initialdir=self.initialdir)) or '.', quiet=0) def createDataArea(self): # Create data area where data entry widgets are placed. self.dataArea = self.app.createcomponent('dataarea', (), None, Frame, (self.app._hull,), relief=SUNKEN, bd=1) self.dataArea.pack(side=LEFT, fill=BOTH, expand=YES, padx=1, pady=1) def destroyDataArea(self): self.app.destroycomponent('dataarea') def createCommandArea(self): # Create a command area for application-wide buttons. self.commandFrame = self.app.createcomponent('commandframe', (), None, Frame,(self.app._hull,),relief=SUNKEN,bd=1) self.commandFrame.place(width=500) self.commandFrame.pack(side=TOP, expand=NO, fill=BOTH, padx=1, pady=1) def destroyCommandArea(self): self.app.destroycomponent('commandframe') def createMessageBar(self): self.messageBar = Pmw.MessageBar(self.commandFrame, entry_width = 25, entry_relief='sunken', entry_borderwidth=1) #, labelpos = 'w') self.abortButton=Button(self.commandFrame, text='Rebuild',highlightthickness=0, # state=DISABLED, command=lambda s=self:self.rebuild(),padx=0,pady=0) self.abortButton.pack(side=RIGHT,fill=BOTH,expand=YES) self.messageBar.pack(side=BOTTOM, anchor=W, fill=X, expand=1) self.balloon.configure(statuscommand = self.messageBar.helpmessage) def destroyMessageBar(self): self.messageBar.destroy() def get_current_session_file(self): session_file = self.cmd.get_setting_text("session_file") session_file = session_file.replace("\\","/") # always use unix-like path separators return session_file def set_current_session_file(self, session_file): session_file = session_file.replace("\\","/") # always use unix-like path separators self.cmd.set("session_file",session_file) def confirm_quit(self,e=None): if self.cmd.get_setting_boolean("session_changed"): session_file = self.get_current_session_file() if session_file != '': message = "Save the current session '%s'?"%os.path.split(session_file)[1] else: message = "Save the current session?" check = tkMessageBox._show("Save Session", message, tkMessageBox.QUESTION, tkMessageBox.YESNOCANCEL) if check==tkMessageBox.YES: if self.session_save(): self.quit_app() elif check==tkMessageBox.NO: self.quit_app() else: self.quit_app() def quit_app(self): self.cmd.log_close() self.cmd.quit() # avoid logging this - it is inconvenient... def buttonAdd(self,frame,text,cmmd): newBtn=Button(frame, text=text,highlightthickness=0, command=cmmd,padx=0,pady=0) newBtn.pack(side=LEFT,fill=BOTH,expand=YES) return newBtn def get_view(self): self.cmd.get_view(2, quiet=0) try: str = self.cmd.get_view(3,quiet=1) self.root.clipboard_clear() self.root.clipboard_append(str) self.last_view = str self.app.selection_clear() self.app.selection_own() self.app.selection_handle(lambda a,b,s=self:s.last_view) print(" PyMOL: Viewing matrix copied to clipboard.") except: traceback.print_exc() def createButtons(self): self.buttonArea = Frame(self.root) self.buttonArea.pack(side=TOP, anchor=W) row1 = self.app.createcomponent('row1', (), None, Frame,self.commandFrame,bd=0) row1.pack(side=TOP,fill=BOTH,expand=YES) btn_reset = self.buttonAdd(row1,'Reset',lambda s=self: s.cmd.do("_ reset")) btn_reset = self.buttonAdd(row1,'Zoom',lambda s=self: s.cmd.do("_ zoom animate=-1")) btn_orient = self.buttonAdd(row1,'Orient',lambda s=self: s.cmd.do("_ orient animate=1")) btn_rtrace = self.buttonAdd(row1,'Draw',lambda s=self: s.cmd.do("_ draw")) btn_rtrace = self.buttonAdd(row1,'Ray',lambda s=self: s.cmd.do("_ ray async=1")) row2 = self.app.createcomponent('row2', (), None, Frame,self.commandFrame,bd=0) row2.pack(side=TOP,fill=BOTH,expand=YES) btn_unpick = self.buttonAdd(row2,'Unpick',lambda s=self: s.cmd.do("_ unpick")) btn_hidesele = self.buttonAdd(row2,'Deselect', lambda: self.cmd.do("_ deselect")) btn_reset = self.buttonAdd(row2,'Rock',lambda s=self: s.cmd.do("_ rock")) btn_getview = self.buttonAdd(row2,'Get View',lambda s=self: s.get_view()) # doesn't get logged row3 = self.app.createcomponent('row3', (), None, Frame,self.commandFrame,bd=0) row3.pack(side=TOP,fill=BOTH,expand=YES) btn_rewind = self.buttonAdd(row3,'|<',lambda s=self: s.cmd.do("_ rewind")) btn_back = self.buttonAdd(row3,'<',lambda s=self: s.cmd.do("_ backward")) btn_stop = self.buttonAdd(row3,'Stop',lambda s=self: s.cmd.do("_ mstop")) btn_play = self.buttonAdd(row3,'Play',lambda s=self: s.cmd.do("_ mplay")) btn_forward = self.buttonAdd(row3,'>',lambda s=self: s.cmd.do("_ forward")) btn_last = self.buttonAdd(row3,'>|',lambda s=self: s.cmd.do("_ ending")) btn_ccache = self.buttonAdd(row3,'MClear',lambda s=self: s.cmd.do("_ mclear")) row4 = self.app.createcomponent('row4', (), None, Frame,self.commandFrame,bd=0) row4.pack(side=TOP,fill=BOTH,expand=YES) self.cmdB = self.buttonAdd(row4,'Command', lambda s=self: s.toggleFrame(s.cmdFrame)) self.buildB = self.buttonAdd(row4,'Builder', lambda s=self: s.toggleFrame(s.buildFrame)) self.volB = self.buttonAdd(row4, 'Volume', self.newVolumeFrame) # initialize disabled self.volB.config(state=DISABLED) def newVolumeFrame(self): volumes = self.cmd.get_names_of_type("object:volume", public=1) if not volumes: return if len(volumes) == 1: self.cmd.volume_panel(volumes[0]) return def callback(): sels = listbox.getcurselection() if sels: self.cmd.volume_panel(sels[0]) window.destroy() title = 'Select a volume object' window = Toplevel(self.app.root) window.title(title) listbox = Pmw.ScrolledListBox(window, labelpos='nw', label_text=title, items=volumes, selectioncommand=callback) listbox.pack(padx=5, pady=5) x, y = window.winfo_pointerxy() window.geometry('+%d+%d' % (x - 20, y - 20)) def destroyButtonArea(self): self.app.destroycomponent('row1') self.app.destroycomponent('row2') self.app.destroycomponent('row3') self.app.destroycomponent('row4') self.buttonArea.destroy() def my_show(self,win,center=1): win.show() def my_withdraw(self,win): if sys.platform!='linux2': win.withdraw() else: win.destroy() def my_activate(self,win,center=1,focus=None): if sys.platform!='linux2': win.activate() else: # autocenter, deiconify, and run mainloop # this is a workaround for a bug in the # interaction between Tcl/Tk and common Linux # window managers (namely KDE/Gnome) which causes # an annoying 1-2 second delay in opening windows! if center: tw = win.winfo_reqwidth()+100 th = win.winfo_reqheight()+100 vw = win.winfo_vrootwidth() vh = win.winfo_vrootheight() x = max(0,(vw-tw)/2) y = max(0,(vh-tw)/2) win.geometry(newGeometry="+%d+%d"%(x,y)) win.deiconify() if focus!=None: focus.focus_set() win.mainloop() def my_deactivate(self,win): if sys.platform!='linux2': win.deactivate() else: # autocenter, deiconify, and run mainloop win.destroy() def doAsync(self,cmmd): t = threading.Thread(target=_doAsync,args=(self.cmd,cmmd)) t.setDaemon(1) t.start() def command_get(self): return self.command.get() def command_set(self, v): return self.command.set(v) def command_set_cursor(self, i): self.entry.icursor(i) def dump(self,event): print(dir(event)) print(event.keysym, event.keycode) def createConsole(self): self.command = StringVar() self.lineCount = 0 self._setup_history() self.cmdFrame = Frame(self.dataArea) self.buildFrame = Builder(self.app, self.dataArea) self.toggleFrame(self.cmdFrame,startup=1) self.entryFrame = Frame(self.cmdFrame) self.entryFrame.pack(side=BOTTOM,expand=NO,fill=X) self.entry_label = Label(self.entryFrame, text="PyMOL>", padx=1, pady=1, justify=RIGHT) self.entry_label.pack(side=LEFT,expand=NO,fill=X) self.entry = Entry(self.entryFrame, justify=LEFT, width=70, textvariable=self.command) self.entry.pack(side=LEFT,expand=YES,fill=X) self.output = Pmw.ScrolledText(self.cmdFrame) self.output.pack(side=TOP, fill=BOTH, expand=YES) self.entry.bind('<Return>', lambda e, s=self: (s.doTypedCommand(s.command.get()), s.command.set(''))) self.entry.bind('<Tab>', lambda e, s=self: s.complete(e)) self.entry.bind('<Up>', lambda e, s=self: s.back()) self.entry.bind('<Down>', lambda e, s=self: s.forward()) self.entry.bind('<Control-Up>', lambda e: self.back_search()) self.root.protocol("WM_DELETE_WINDOW", lambda s=self: s.confirm_quit()) self.log_file = "log.pml" # self.entry = self.app.createcomponent('entry', (), None, # Entry, # (self.dataArea,), # justify=LEFT, # width=50, ### textvariable=self.command) text = self.output.component('text') self.text = text if sys.platform.startswith('win'): self.font = 'lucida console' # only available on windows self.my_fw_font=(self.font,8) self.fixedfont.configure(family=self.font, size=self.my_fw_font[1]) else: text.tk.call('tk','scaling',1) self.font = 'fixed' # should be available on any X11-based platform self.my_fw_font=(self.font,10) if sys.platform == 'darwin': self.fixedfont.configure(size=11) text.configure(width=74) self.balloon.bind(self.entry, '''Command Input Area Get the list of commands by hitting <TAB> Get the list of arguments for one command with a question mark: PyMOL> color ? Read the online help for a command with "help": PyMOL> help color Get autocompletion for many arguments by hitting <TAB> PyMOL> color ye<TAB> (will autocomplete "yellow") ''') if self.app.allow_after: self.output.after(100,self.update_feedback) self.output.after(100,self.update_menus) self.output.pack(side=BOTTOM,expand=YES,fill=BOTH) self.app.bind(self.entry, 'Command Input Area') self.app.bind_all('<F1>',lambda e,s=self: s.cmd.do("cmd._special(1,0,0)")) self.app.bind_all('<F2>',lambda e,s=self: s.cmd.do("cmd._special(2,0,0)")) self.app.bind_all('<F3>',lambda e,s=self: s.cmd.do("cmd._special(3,0,0)")) self.app.bind_all('<F4>',lambda e,s=self: s.cmd.do("cmd._special(4,0,0)")) self.app.bind_all('<F5>',lambda e,s=self: s.cmd.do("cmd._special(5,0,0)")) self.app.bind_all('<F6>',lambda e,s=self: s.cmd.do("cmd._special(6,0,0)")) self.app.bind_all('<F7>',lambda e,s=self: s.cmd.do("cmd._special(7,0,0)")) self.app.bind_all('<F8>',lambda e,s=self: s.cmd.do("cmd._special(8,0,0)")) self.app.bind_all('<F9>',lambda e,s=self: s.cmd.do("cmd._special(9,0,0)")) self.app.bind_all('<F10>',lambda e,s=self: s.cmd.do("cmd._special(10,0,0)")) self.app.bind_all('<F11>',lambda e,s=self: s.cmd.do("cmd._special(11,0,0)")) self.app.bind_all('<F12>',lambda e,s=self: s.cmd.do("cmd._special(12,0,0)")) self.app.bind_all('<Control-F1>',lambda e,s=self: s.cmd.do("cmd._special(1,0,0,2)")) self.app.bind_all('<Control-F2>',lambda e,s=self: s.cmd.do("cmd._special(2,0,0,2)")) self.app.bind_all('<Control-F3>',lambda e,s=self: s.cmd.do("cmd._special(3,0,0,2)")) self.app.bind_all('<Control-F4>',lambda e,s=self: s.cmd.do("cmd._special(4,0,0,2)")) self.app.bind_all('<Control-F5>',lambda e,s=self: s.cmd.do("cmd._special(5,0,0,2)")) self.app.bind_all('<Control-F6>',lambda e,s=self: s.cmd.do("cmd._special(6,0,0,2)")) self.app.bind_all('<Control-F7>',lambda e,s=self: s.cmd.do("cmd._special(7,0,0,2)")) self.app.bind_all('<Control-F8>',lambda e,s=self: s.cmd.do("cmd._special(8,0,0,2)")) self.app.bind_all('<Control-F9>',lambda e,s=self: s.cmd.do("cmd._special(9,0,0,2)")) self.app.bind_all('<Control-F10>',lambda e,s=self: s.cmd.do("cmd._special(10,0,0,2)")) self.app.bind_all('<Control-F11>',lambda e,s=self: s.cmd.do("cmd._special(11,0,0,2)")) self.app.bind_all('<Control-F12>',lambda e,s=self: s.cmd.do("cmd._special(12,0,0,2)")) self.entry.bind('<Prior>',lambda e,s=self: s.cmd.do("cmd._special(104,0,0)")) self.entry.bind('<Next>',lambda e,s=self: s.cmd.do("cmd._special(105,0,0)")) self.entry.bind('<Control-Prior>',lambda e,s=self: s.cmd.do("cmd._special(104,0,0,2)")) self.entry.bind('<Control-Next>',lambda e,s=self: s.cmd.do("cmd._special(105,0,0,2)")) self.entry.bind('<Home>',lambda e,s=self: s.cmd.do("cmd._special(106,0,0)")) self.entry.bind('<End>',lambda e,s=self: s.cmd.do("cmd._special(107,0,0)")) def update_feedback(self): feedback = self.cmd._get_feedback(self.cmd) if feedback!=None: self.text.configure(state='normal') for a in feedback: self.output.insert(END,"\n") self.output.insert(END,a) self.output.see(END) self.lineCount = self.lineCount + 1 if self.lineCount > 10000: self.output.delete('0.0','%i.%i' % (self.lineCount-5000,0)) self.lineCount=5000 self.text.configure(state='disabled') progress = self.cmd.get_progress() if progress>=0.0: # self.abortButton.config(state=NORMAL) self.messageBar.message("busy","Progress %d%%..."%int(progress*100)) else: # self.abortButton.config(state=DISABLED) self.messageBar.resetmessages("busy") if self.app.allow_after: if feedback == None: # PyMOL busy, so try more aggressively to get lock self.output.after(10,self.update_feedback) # 100X a second else: self.output.after(100,self.update_feedback) # 10X a second def abort(self): self.cmd.interrupt() # self.abortButton.config(state=DISABLED) def rebuild(self): self.doAsync("_ rebuild") def toggleFrame(self, frame, startup=0): if frame not in self.dataArea.slaves(): # clear all frames in dataArea for f in self.dataArea.slaves(): f.pack_forget() # add requested frame to data area frame.pack(side=BOTTOM, fill=BOTH, expand=YES) else: # clear frame from data area if frame != self.cmdFrame: frame.pack_forget() # next command will cause command frame to be turned on if # nothing else is visible... might not want this behavior self.cmdFrame.pack(side=BOTTOM, fill=BOTH, expand=YES) frame = self.cmdFrame if not startup: if frame == self.cmdFrame: if self.edit_mode != None: self.cmd.edit_mode(self.edit_mode) self.edit_mode = None if self.auto_overlay != None: self.cmd.set("auto_overlay",self.auto_overlay) self.auto_overlay = None if self.valence != None: self.cmd.set("valence",self.valence) elif frame == self.buildFrame: frame.deferred_activate() if "Editing" not in self.cmd.get("button_mode_name"): self.cmd.edit_mode(1) self.edit_mode = 0 self.valence = self.cmd.get("valence") self.cmd.set("valence","1") self.auto_overlay = self.cmd.get("auto_overlay") self.cmd.set("auto_overlay",1) def update_menus(self): self.setting.refresh() if True: # volume frame is closed, update the button if len(self.cmd.get_names_of_type("object:volume",public=1))>0: self.volB.config(state=NORMAL) else: self.volB.config(state=DISABLED) # keep calling if self.app.allow_after: self.output.after(500,self.update_menus) # twice a second def file_open(self,tutorial=0): if not tutorial: initdir = self.initialdir ftypes = self.app.getLoadableFileTypes() else: initdir = os.environ['TUT'] # only list file extensions that are used for tutorial data ftypes = [("Tutorial Data","*.pdb"),] if TkVersion>8.3: ofile_list = askopenfilename(initialdir = initdir, filetypes=ftypes, multiple=1) # new option in Tk 8.4 else: ofile_list = [ askopenfilename(initialdir = initdir, filetypes=ftypes) ] for ofile in ofile_list: if len(ofile): if not tutorial: self.initialdir = os.path.dirname(ofile) try: if ofile[-4:].lower() == '.pse' and ofile != self.save_file: self.save_file = '' # remove ambiguous default self.cmd.do('_ /cmd.load(%s, quiet=0)' % repr(ofile)) except self.pymol.CmdException: print("Error: unable to open file '%s'"%ofile) def log_open(self): sfile = asksaveasfilename(initialfile = self.log_file, initialdir = self.initialdir, filetypes=[ ("PyMOL Script","*.pml"), ("PyMOL Program","*.pym"), ("Python Program","*.py"), ("All Files","*.*"), ("All Files","*"), ]) if len(sfile): self.initialdir = os.path.dirname(sfile) self.log_file = os.path.basename(sfile) self.cmd.log_open(sfile) def log_resume(self,append_only=0): ofile = askopenfilename(initialdir = os.getcwd(), filetypes=[("All Resumable","*.pml"), ("All Resumable","*.pym"), ("All Resumable","*.py"), ("PyMOL Script","*.pml"), ("PyMOL Program","*.pym"), ("Python Program","*.py"), ("All Files","*.*"), ("All Files","*"), ]) if len(ofile): self.initialdir = os.path.dirname(ofile) self.log_file = os.path.basename(ofile) # os.chdir(self.initialdir) self.cmd.resume(ofile) def log_append(self,append_only=0): ofile = askopenfilename(initialdir = os.getcwd(), filetypes=[("All Appendable","*.pml"), ("All Appendable","*.pym"), ("All Appendable","*.py"), ("PyMOL Script","*.pml"), ("PyMOL Program","*.pym"), ("Python Program","*.py"), ("All Files","*.*"), ("All Files","*"), ]) if len(ofile): self.initialdir = os.path.dirname(ofile) self.log_file = os.path.basename(ofile) # os.chdir(self.initialdir) self.cmd.log_open(ofile,'a') def session_save(self): self.save_file = self.get_current_session_file() if self.save_file!='': self.cmd.log("save %s,format=pse\n"%(self.save_file), "cmd.save('%s',format='pse')\n"%(self.save_file)) # self.cmd.save(self.save_file,"","pse",quiet=0) # self.cmd.set("session_changed",0) self.cmd.do("_ cmd.save('''%s''','','pse',quiet=0)"%self.save_file) # do this in the main thread to block cmd.quit, etc. self.cmd.do("_ cmd.set('session_changed',0)") return 1 else: return self.session_save_as() def session_save_as(self): (self.initialdir, self.save_file) = os.path.split(self.get_current_session_file()) (save_file, def_ext) = os.path.splitext(self.save_file) sfile = asksaveasfilename(defaultextension = _def_ext(def_ext), initialfile = save_file, initialdir = self.initialdir, filetypes=[ ("PyMOL Session File","*.pse"), ("PyMOL Show File","*.psw"), ]) if len(sfile): if re.search(r"\.pse$|\.PSE$|\.psw$|\.PSW$",sfile)==None: sfile=sfile+".pse" self.initialdir = os.path.dirname(sfile) self.cmd.log("save %s,format=pse\n"%(sfile), "cmd.save('%s',format='pse')\n"%(sfile)) # self.cmd.save(sfile,"",format='pse',quiet=0) # self.cmd.set("session_changed",0) self.save_file = sfile # self.cmd.set("session_file",self.save_file) self.set_current_session_file(self.save_file) # do this in the main thread to block cmd.quit, etc. self.cmd.do("_ cmd.save('''%s''','','pse',quiet=0)"%self.save_file) self.cmd.do("_ cmd.set('session_changed',0)") return 1 else: return 0 def file_save(self): """ File->Save Molecule, now with filtering """ def command(result): if result == 'OK': self.file_save2( dialog.getcurselection(), multiple_files_option.getvalue(), states_option.getvalue()) self.my_withdraw(dialog) def update_save_listbox(): lst = self.cmd.get_names('public') searchstr = filter_entry.getvalue() if searchstr: lst = [x for x in lst if searchstr in x] dialog.component("scrolledlist").setlist(lst) dialog = Pmw.SelectionDialog(self.root, title="Save", buttons = ('OK', 'Cancel'), defaultbutton='OK', scrolledlist_labelpos=N, scrolledlist_listbox_selectmode=EXTENDED, label_text='Which object or selection would you like to save?', scrolledlist_items = (), # used to be 'lst' command = command) filter_entry = Pmw.EntryField(dialog.interior(), labelpos='w', modifiedcommand=update_save_listbox, validate=None, value="", label_text="Filter:") filter_entry.pack(pady=6, fill='x', expand=0, padx=10) multiple_files_option = Pmw.RadioSelect( dialog.interior(), labelpos='w', orient='vertical', selectmode='single', label_text="Save to...", buttontype="radiobutton", ) multiple_files_option.add("one file") multiple_files_option.add("multiple files") multiple_files_option.invoke("one file") multiple_files_option.pack(side='left', pady=8) states_option = Pmw.RadioSelect( dialog.interior(), labelpos='w', orient='vertical', selectmode='single', label_text='Saved state...', buttontype="radiobutton" ) states_option.add("all") states_option.add("global") states_option.add("object's current") states_option.invoke("global") states_option.pack(side='right', pady=8) # The listbox is created empty. Fill it now. update_save_listbox() if len(dialog.component('scrolledlist').get()): # set focus on the first item listbox = dialog.component('scrolledlist') listbox.selection_set(0) self.my_show(dialog) def file_save2(self, sels, multiple_files_flag, state_flag): filetypes_save = [ ("PDB File","*.pdb"), ("MOL File","*.mol"), ("MOL2 File","*.mol2"), ("MMD File","*.mmd"), ("PKL File","*.pkl"), ("SDF File","*.sdf"), ("PDBx/mmCIF","*.cif"), ("PQR","*.pqr"), ("Maestro","*.mae"), ("XYZ","*.xyz"), ] if True: # save N>1 objects to ONE file if multiple_files_flag == "one file" and len(sels)>=1: sfile = '_'.join(sels) if len(sels) < 3 else \ sels[0] + '-and-%d-more' % (len(sels) - 1) sfile = asksaveasfilename(defaultextension = _def_ext(".pdb"), initialfile = sfile, initialdir = self.initialdir, filetypes=filetypes_save) if len(sfile): # maybe use PDBSTRs for saving multiple files to multiple states self.initialdir = os.path.dirname(sfile) save_sele = ' or '.join(["("+str(x)+")" for x in sels]) self.cmd.log("save %s,(%s)\n"%(sfile,save_sele), "cmd.save('%s','(%s)')\n"%(sfile,save_sele)) if state_flag == "all": self.cmd.save(sfile,"(%s)"%save_sele,state=0,quiet=0) elif state_flag == "object's current": ap = 0 for sel in sels: s = int(self.cmd.get("state", str(sel))) self.cmd.multisave(sfile,str(sel),state=s, quiet=0, append=ap) ap = 1 else: self.cmd.save(sfile,"(%s)"%save_sele,quiet=0) return else: # save to many files for curName in sels: ## print "Result is: ", result ## print "Sels is: ", sels ## print "CurName is: ", curName ## print "State flag is: ", state_flag # The only special case for saving files is when the user selects a multi-state object # and wants to save that to multiple files, each state in one file. doSplit=False if state_flag=='all': stateSave = "0" if len(sels)==1: # print "User wants to split a file" doSplit=True elif state_flag=='global': stateSave = self.cmd.get_state() elif state_flag=="object's current": stateSave = int(self.cmd.get("state",curName)) # print "Saving curren't object's state as: ", stateSave else: # default to current global stateSave = "state=", self.cmd.get_state() if True: sfile = asksaveasfilename(defaultextension = _def_ext(".pdb"), initialfile = curName, initialdir = self.initialdir, filetypes = filetypes_save) # now save the file (customizing states as necessary) # print "sfile is: ", sfile if len(sfile): # maybe use PDBSTRs for saving multiple files to multiple states self.initialdir = os.path.dirname(sfile) save_sele = str("("+curName+")") if doSplit: # save each state in "save_sele" to file "sfile" as 'sfile_stateXYZ.pdb' s = self.cmd.count_states(save_sele) for stateSave in range(1,int(s)+1): save_file = sfile # _state004 inter = "_state" + str(stateSave).zfill(len(str(s))+1) # g either MATCHES *.pdb or not. If so, save, name_stateXYZ.pdb g = re.search("(.*)(\..*)$", save_file) if g!=None: # 1PDB_state004.pdb save_file = g.groups()[0] + inter + g.groups()[1] else: # user entered a file w/o an extension name: eg, '1abc' # this saves to, '1abc_state00XYZ' save_file = save_file + inter self.cmd.log("save %s,(%s)\n"%(save_file,save_sele), "cmd.save('%s','(%s)', state='%s')\n"%(save_file,save_sele,stateSave)) self.cmd.save(save_file,"(%s)"%save_sele,state=stateSave,quiet=0) else: save_file = sfile # just save current selection to one file self.cmd.log("save %s,(%s)\n"%(save_file,save_sele), "cmd.save('%s','(%s)', state='%s')\n"%(save_file,save_sele,stateSave)) self.cmd.save(save_file,"(%s)"%save_sele,state=stateSave,quiet=0) def edit_pymolrc(self): from pmg_tk import TextEditor TextEditor.edit_pymolrc(self) def file_run(self): ofile = askopenfilename(initialdir = os.getcwd(), filetypes=[("All Runnable","*.pml"), ("All Runnable","*.pym"), ("All Runnable","*.py"), ("All Runnable","*.pyc"), ("PyMOL Script","*.pml"), ("Python Program","*.py"), ("Python Program","*.pyc"), ("PyMOL Program","*.pym"), ("All Files","*.*"), ("All Files","*"), ]) if len(ofile): self.__script__ = ofile if re.search("\.pym*$|\.PYM*$",ofile): self.cmd.do("run "+ofile); else: self.cmd.do("@"+ofile); def file_save_png(self): sfile = asksaveasfilename(defaultextension = _def_ext(".png"), initialdir = self.initialdir, filetypes=[("PNG File","*.png")]) if len(sfile): self.initialdir = os.path.dirname(sfile) self.cmd.log("png %s\n"%sfile,"cmd.png('%s')\n"%sfile) self.cmd.png(sfile,quiet=0) def file_save_wrl(self): sfile = asksaveasfilename(defaultextension = _def_ext(".wrl"), initialdir = self.initialdir, filetypes=[("VRML 2 WRL File","*.wrl")]) if len(sfile): self.initialdir = os.path.dirname(sfile) self.cmd.log("save %s\n"%sfile,"cmd.save('%s')\n"%sfile) self.cmd.save(sfile,quiet=0) def file_save_dae(self): sfile = asksaveasfilename(defaultextension = _def_ext(".dae"), initialdir = self.initialdir, filetypes=[("COLLADA File","*.dae")]) if len(sfile): self.initialdir = os.path.dirname(sfile) self.cmd.log("save %s\n"%sfile,"cmd.save('%s')\n"%sfile) self.cmd.save(sfile,quiet=0) def file_save_pov(self): sfile = asksaveasfilename(defaultextension = _def_ext(".pov"), initialdir = self.initialdir, filetypes=[("POV File","*.pov")]) if len(sfile): self.initialdir = os.path.dirname(sfile) self.cmd.log("save %s\n"%sfile,"cmd.save('%s')\n"%sfile) self.cmd.save(sfile,quiet=0) def file_save_mpeg(self): try: from freemol import mpeg_encode if not mpeg_encode.validate(): print("produce-error: Unable to validate freemol.mpeg_encode") raise except: tkMessageBox.showerror("Error", "MPEG encoder missing.\nThe FreeMOL add-ons may not be installed.") return def command(value): mQual = int(w_quality.get()) mode = 'ray' if w_ray.get() else 'draw' viewport = int(w_viewport[0].get()), int(w_viewport[1].get()) dialog.destroy() if value != 'OK': return sfile = asksaveasfilename(defaultextension = _def_ext(".mpg"), initialdir = self.initialdir, filetypes=[("MPEG movie file","*.mpg")]) if len(sfile): self.initialdir = os.path.dirname(sfile) mQual = self.cmd.get_setting_int("movie_quality") self.cmd.log("movie.produce %s,quality=%d,quiet=0\n"%(sfile,mQual), "cmd.movie.produce('''%s''',quality=%d,quiet=0)\n"%(sfile,mQual)) self.cmd.movie.produce(sfile, mode, quality=mQual, quiet=0, width=viewport[0], height=viewport[1]) dialog = Pmw.Dialog(title='Movie Settings', buttons=('OK', 'Cancel'), defaultbutton='OK', command=command) parent = dialog.interior() gridkw = {'padx': 5, 'pady': 5, 'sticky': W, 'row': 0} Label(parent, text='Encoding Quality (0-100)',).grid(column=0, **gridkw) w_quality = Pmw.Counter(parent, entryfield_value=self.cmd.get_setting_int("movie_quality"), entryfield_validate={'validator': 'integer', 'min': 0, 'max': 100}) w_quality.grid(column=1, **gridkw) gridkw['row'] += 1 Label(parent, text='Ray Trace Frames').grid(column=0, **gridkw) w_ray = BooleanVar(parent, self.cmd.get_setting_boolean('ray_trace_frames')) Checkbutton(parent, variable=w_ray).grid(column=1, **gridkw) w_viewport = [] for text, value in zip(('Width', 'Height'), self.cmd.get_viewport()): gridkw['row'] += 1 Label(parent, text=text + ' (pixels)').grid(column=0, **gridkw) w = Pmw.Counter(parent, entryfield_value=value, entryfield_validate={'validator': 'integer', 'min': 0}) w.grid(column=1, **gridkw) w_viewport.append(w) def file_save_mpng(self): sfile = asksaveasfilename(initialdir = self.initialdir, filetypes=[("Numbered PNG Files","*.png")]) if len(sfile): self.initialdir = os.path.dirname(sfile) self.cmd.log("mpng %s\n"%sfile,"cmd.mpng('%s')\n"%sfile) self.cmd.mpng(sfile,modal=-1) def cat_terms(self): for path in [ "$PYMOL_PATH/LICENSE.txt", "$PYMOL_PATH/LICENSE.TXT", "$PYMOL_PATH/LICENSE" ]: path = self.pymol.cmd.exp_path(path) if os.path.exists(path): print(open(path).read().strip()) return print(" Error: no license terms found.") def toggleClickThrough(self, toggle): if toggle: os.system( "defaults write com.apple.x11 wm_click_through -bool true") os.system( "defaults write org.x.X11 wm_click_through -bool true") os.system( "defaults write com.apple.x11 wm_ffm -bool true") os.system( "defaults write org.x.X11 wm_ffm -bool true") print("Enabled wm_click_through and wm_ffm.", end=' ') else: os.system( "defaults write com.apple.x11 wm_click_through -bool false") os.system( "defaults write org.x.X11 wm_click_through -bool false") os.system( "defaults write com.apple.x11 wm_ffm -bool false") os.system( "defaults write org.x.X11 wm_ffm -bool false") print("Disabled wm_click_through and wm_ffm.", end=' ') print("Please restart X11.") def createMenuBar(self): self.menuBar = Pmw.MenuBar(self.root, balloon=self.balloon, hull_relief=RAISED, hull_borderwidth=1) self.menuBar.pack(fill=X) addmenuitem = self.menuBar.addmenuitem addcascademenu = self.menuBar.addcascademenu self.setting = Setting(self.app) def _addmenu(data, parent): for item in data: if item[0] == 'separator': addmenuitem(parent, 'separator', '') elif item[0] == 'menu': label = item[1] menulabel = parent + '/' + label self.menuBar.addcascademenu(parent, menulabel, label, label=label, tearoff=FALSE) _addmenu(item[2], menulabel) elif item[0] == 'command': label = item[1] command = item[2] if command is None: if DEBUG: print('warning: skipping', label, parent) else: if isinstance(command, str): command = lambda c=command: self.cmd.do(c) addmenuitem(parent, 'command', label, label=label, command=command) elif item[0] == 'check': label = item[1] var = getattr(self.setting, item[2]) if len(item) > 4: addmenuitem(parent, 'checkbutton', label, label=label, variable=var, onvalue=item[3], offvalue=item[4]) else: addmenuitem(parent, 'checkbutton', label, label=label, variable=var) elif item[0] == 'radio': label = item[1] var = getattr(self.setting, item[2]) value = item[3] addmenuitem(parent, 'radiobutton', label=label, value=value, variable=var) elif DEBUG: print('error:', item) for _, label, data in self.get_menudata(): assert _ == 'menu' self.menuBar.addmenu(label, label, tearoff=TRUE) _addmenu(data, label) # self.menuBar.addmenuitem('Help', 'command', 'Release Notes', # label='Release Notes', # command = lambda s=self: s.cmd.do("_ cmd.show_help('release')")) # self.menuBar.addmenuitem('Help', 'separator', '') # self.menuBar.addmenuitem('Help', 'command', 'Help on Commands', # label='Commands', # command = lambda s=self: s.cmd.do("_ cmd.show_help('commands')")) # self.menuBar.addmenuitem('Help', 'command', 'Help on Launching', # label='Launching', # command = lambda s=self: s.cmd.do("_ cmd.show_help('launching')")) # self.menuBar.addmenuitem('Help', 'separator', '') # self.menuBar.addmenuitem('Help', 'command', 'Help on Selections', # label='Select Command', # command = lambda s=self: s.cmd.do("_ cmd.show_help('select')")) # self.menuBar.addmenuitem('Help', 'command', 'Help on Selections', # label='Selection Syntax', # command = lambda s=self: s.cmd.do("_ cmd.show_help('selections')")) # self.menuBar.addmenuitem('Help', 'command', 'Example Selections', # label='Selection Examples', # command = lambda s=self: s.cmd.do("_ cmd.show_help('examples')")) # self.menuBar.addmenuitem('Help', 'separator', '') # self.menuBar.addmenuitem('Help', 'command', 'Help on the Mouse', # label='Mouse', # command = lambda s=self: s.cmd.do("_ cmd.show_help('mouse')")) # self.menuBar.addmenuitem('Help', 'command', 'Help on the Keyboard', # label='Keyboard', # command = lambda s=self: s.cmd.do("_ cmd.show_help('keyboard')")) # self.menuBar.addmenuitem('Help', 'command', 'Help on Molecular Editing', # label='Molecular Editing', # command = lambda s=self: s.cmd.do("_ cmd.show_help('editing')")) # self.menuBar.addmenuitem('Help', 'command', 'Help on Molecular Editing', # label='Molecular Editing Keys', # command = lambda s=self: s.cmd.do("_ cmd.show_help('edit_keys')")) # self.menuBar.addmenuitem('Help', 'command', 'Help on Stereo', # label='Stereo', # command = lambda s=self: s.cmd.do("_ cmd.show_help('stereo')")) # self.menuBar.addmenuitem('Help', 'separator', '') # self.menuBar.addmenuitem('Help', 'command', 'Help on the API', # label='API', # command = lambda s=self: s.cmd.do("_ cmd.show_help('api')")) # self.toggleBalloonVar = IntVar() # self.toggleBalloonVar.set(0) # self.menuBar.addmenuitem('Help', 'separator', '') # self.menuBar.addmenuitem('Help', 'checkbutton', # 'Toggle balloon help', # label='Balloon help', # variable = self.toggleBalloonVar, # command=self.toggleBalloon) if sys.platform == 'win32' and self.app.pymol.invocation.options.incentive_product: self.menuBar.addmenuitem('Edit', 'separator', '') self.menuBar.addmenuitem('Edit', 'command', 'Copy Image', label='Copy Image to Clipboard', command = lambda s=self:s.cmd.copy_image(quiet=0)) self.menuBar.addmenuitem('Edit', 'checkbutton', 'Auto-Copy Images', label='Auto-Copy Images', variable = self.setting.auto_copy_images, ) self.menuBar.addmenuitem('Edit', 'separator', '') self.menuBar.addmenuitem('Edit', 'command', 'To Copy Text: Use Ctrl-C in TclTk GUI', label='To copy text use Ctrl-C in the TclTk GUI', state='disabled', command = None) self.menuBar.addmenuitem('Edit', 'command', 'To Paste Text, Use Ctrl-V in TclTk GUI', label='To paste text use Ctrl-V in the TckTk GUI', state='disabled', command = None) if sys.platform == 'darwin': self.menuBar.addmenuitem('Mouse', 'separator', '') self.menuBar.addcascademenu('Mouse', 'MacX11Focus', 'Mac OS X11', label='Mac OS X11') self.menuBar.addmenuitem('MacX11Focus', 'command', 'Enable Click Through', label='Enable Click Through', command = lambda s=self: s.toggleClickThrough(1)) self.menuBar.addmenuitem('MacX11Focus', 'command', 'Disable Click Through', label='Disable Click Through', command = lambda s=self: s.toggleClickThrough(0)) # hook up scene menu updates index = self.pymol.setting.index_dict.get('scenes_changed') self.setting.active_dict[index] = self.update_scene_menu def settings_edit_all_dialog(self): SetEditor(self) def edit_colors_dialog(self): ColorEditor(self) def update_scene_menu(self): scene_list = self.cmd.get_scene_list() for action in ['recall', 'clear']: parent = 'Scene/' + action.capitalize() self.menuBar.deletemenuitems(parent, 0, 999) for k in scene_list: self.menuBar.addmenuitem(parent, 'command', k, label=k, command=lambda k=k, a=action: self.cmd.scene(k, a)) parent = 'Scene/Store' self.menuBar.deletemenuitems(parent, 0, 11) for i in range(12): k = 'F' + str(i + 1) self.scene_F_keys[i].set(1 if k in scene_list else 0) self.menuBar.addmenuitem(parent, 'checkbutton', k, label=k, variable=self.scene_F_keys[i], command=lambda k=k: self.cmd.scene(k, 'store')) def show_about(self): Pmw.aboutversion(self.appversion) Pmw.aboutcopyright(self.copyright) Pmw.aboutcontact( 'For more information, browse to: %s\n or send email to: %s' %\ (self.contactweb, self.contactemail)) self.about = Pmw.AboutDialog(self.root, applicationname=self.appname) self.my_activate(self.about) self.about.withdraw() def createInterface(self): self.balloon = Pmw.Balloon(self.root) self.createMenuBar() self.app.menuBar = self.menuBar # to support legacy plugins self.app.initializePlugins() self.createDataArea() self.createCommandArea() self.createButtons() self.createMessageBar() self.createConsole() def setup(self): # call the parent method PMGSkin.setup(self) # name the application self.root.title(self.appname) # create the user interface self.createInterface() # pack the root window self.app._hull.pack(side=LEFT, fill=BOTH, expand=YES, anchor=CENTER) # and set focus if hasattr(self,'entry'): self.entry.focus_set() def takedown(self): self.destroyMessageBar() self.destroyDataArea() self.destroyCommandArea() self.destroyButtonArea() self.balloon.destroy() self.menuBar.destroy() def __init__(self,app): global root root = app.root PMGSkin.__init__(self,app) Normal.appversion = app.pymol.cmd.get_version()[0] Normal.appversion += " Incentive Product" \ if app.pymol.invocation.options.incentive_product else \ " Open-Source" self.app = app self.save_file = '' self.cmd = app.pymol.cmd self.util = app.pymol.util self.movie_command = None self.movie_start = 1 self.auto_overlay = None self.edit_mode = None self.valence = None self._initialdir = '' self.fixedfont = tkFont.nametofont('TkFixedFont') self.scene_F_keys = [IntVar(root) for _ in range(12)] def __init__(app): return Normal(app)