def body(self, guiParent): self.menus = {} self.menu_items = {} self.fixedActiveMenus = {} self.popups = {} self.callbacksDict = {} self.selected_objects = [] self.menubar = menubar = Menu(guiParent) self.font = DEFAULT_FONT self.setProjectMenu() # menu = Menu(self.menubar, tearoff=0) menu.bind('<Button>', self.printAnalysisCommandLineInfo) self.menubar.add_cascade(label='CcpNmr Analysis', shortcut='C', menu=menu) self.menubar.add_command(label='FormatConverter', shortcut='F', command=self.runFormatConverter) self.menubar = menu self.initProject() self.setPeaksMenu() self.setMoleculeMenu() self.setAssignMenu() self.setResonanceMenu() self.setDataMenu() self.setStructureMenu() self.setChartMenu() self.setMacroMenu() self.setOtherMenu() self.setMenuState() # need to do it again because of OtherMenu state # Help Submenu helpMenu = Menu(self.menubar, tearoff=0) helpMenu.add_command(label='Version', shortcut='V', command=self.showVersion) helpMenu.add_command(label='About', shortcut='A', command=self.showAbout) helpMenu.add_command(label='Help', shortcut='H', command=self.showHelp) menu.add_separator() menu.add_command(label='CCPN Updates', shortcut='U', image=self.iconRefresh, compound='left', command=self.updateAnalysis, tipText='Get any new patches and updates to CcpNmr') menu.add_cascade(label='CCPN Help', shortcut='H', image=self.iconHelp, compound='left', menu=helpMenu) self.config(menu=menubar) # Ensure that the first row and column in popup expand guiParent.grid_rowconfigure(0, weight=1) guiParent.grid_columnconfigure(0, weight=1, minsize=200) frame = Frame(guiParent) # Body widgets can be put in this frame frame.grid() softwareOpts = [ 'Extend-NMR', 'ARIA 2', 'Auremol', 'CING', ' ECI ', 'HADDOCK', ' ISD ', 'PRODECOMP' ] self.tabbedFrame = TabbedFrame(guiParent, options=softwareOpts, toggleOff=False, selected=0, callback=self.toggleTab) self.tabbedFrame.grid(row=0, column=0, sticky='nsew') frames = self.tabbedFrame.frames # Logos ccpnDir = getTopDirectory() imageDir = os.path.join(ccpnDir, 'python', 'extendNmr', 'images') imageFile = os.path.join(imageDir, 'Fp6Logo.gif') self.fp6Logo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'CingLogo.gif') self.cingLogo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'AriaLogo.gif') self.ariaLogo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'IsdLogo.gif') self.isdLogo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'HaddockLogo.gif') self.haddockLogo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'AuremolLogo.gif') self.auremolLogo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'CcpnLogo.gif') self.ccpnLogo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'ProdecompLogo.gif') self.prodecompLogo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'MddLogo.gif') self.mddLogo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'BrukerLogo.gif') self.brukerLogo = Tkinter.PhotoImage(file=imageFile) imageFile = os.path.join(imageDir, 'MsdLogo.gif') self.msdLogo = Tkinter.PhotoImage(file=imageFile) self.initExtendNmr(frames[0]) self.initAria(frames[1]) self.initAuremol(frames[2]) self.initCing(frames[3]) self.initEci(frames[4]) self.initHaddock(frames[5]) self.initIsd(frames[6]) self.initProdecomp(frames[7]) self.initProject(self.project) if not self.project: for button in self.projButtons: button.disable() self.geometry('680x670')
def setProjectMenu(self): ProjectMenu = 'Project' # Imports submenu importsMenu = Menu(self.menubar, tearoff=False) importsMenu.add_command(label='Via Format Converter', shortcut='F', command=self.runFormatConverter) importsMenu.add_command(label='NMR-STAR 2.1.1', command=self.importNmrStar211) importsMenu.add_command(label='NMR-STAR 3.1', shortcut='N', command=self.importNmrStar31) importsMenu.add_command(label='PDB 3.20', shortcut='P', command=self.importPdb) importsMenu.add_command(label='Coordinates (PDB-style)', shortcut='C', command=self.importCoordinates) # Preferences submenu fontsMenu = FontMenu(self.menubar, self.selectFont, sizes=(8, 10, 12), doItalic=False, doBoldItalic=False, tearoff=0) prefsMenu = Menu(self.menubar, tearoff=False) prefsMenu.add_cascade( label='Fonts', shortcut='F', image=self.iconFont, compound='left', menu=fontsMenu, tipText='Select font to use in the graphical interface') prefsMenu.add_command(label='Colour Schemes', image=self.iconTable, compound='left', shortcut='C', tipText='Edit and create colour schemes', command=self.editColorSchemes) prefsMenu.add_command( label='Residue Codes', image=self.iconTable, compound='left', shortcut='R', tipText= 'User-specified codes that override the standard residue names', command=self.editResidueCodes) prefsMenu.add_command(label='User Options', image=self.iconTable, compound='left', shortcut='U', tipText='General options for Analysis behaviour', command=self.editProfiles) # menu = Menu(self.menubar, tearoff=0) menu.add_command( label='New', shortcut='N', image=self.iconNewWindow, compound='left', command=self.newProject, tipText='Create a new, blank CCPN project (closes any open project)' ) menu.add_command( label='Open Project', shortcut='O', image=self.iconOpen, compound='left', command=self.openProject, tipText= 'Open a new CCPN project by selecting a project directory on disk') menu.add_command( label='Open Spectra', shortcut='p', image=self.iconOpenFile, compound='left', command=self.openSpectrum, tipText= 'Open spectrum data fom disk, creating a default CCPN project if needed' ) menu.add_command(label='Save', shortcut='S', image=self.iconSave, compound='left', command=self.saveProject, tipText='Save the current CCPN project on disk') menu.add_command( label='Save As', shortcut='A', image=self.iconSaveAs, compound='left', command=self.saveAsProject, tipText= 'Save the current CCPN project under a different name (project directory)' ) menu.add_cascade(label='Import', shortcut='I', image=self.iconImport, compound='left', menu=importsMenu) menu.add_command(label='Close', shortcut='C', image=self.iconClose, compound='left', command=self.closeProject, tipText='Close the current CCPN project') menu.add_command( label='Quit', shortcut='Q', image=self.iconQuit, compound='left', command=self.quit, tipText='Quit Extend-NMR, closing any open CCPN project') menu.add_separator() menu.add_cascade(label='Preferences', shortcut='P', image=self.iconPrefs, compound='left', menu=prefsMenu) menu.add_command( label='Validate', shortcut='V', image=self.iconTool, compound='left', command=self.validateProject, tipText= 'Check the current CCPN project for data model consistency errors') menu.add_command( label='Backup', shortcut='B', image=self.iconTool, compound='left', command=self.backupProject, tipText='Setup options for automated backup of CCPN project data') menu.add_command( label='Archive', shortcut='r', image=self.iconTool, compound='left', command=self.archiveProject, tipText= 'Save the current CCPN project in an archived form, e.g. tar gzipped' ) self.menubar.add_cascade(label=ProjectMenu, shortcut='j', menu=menu) self.menus[ProjectMenu] = menu self.menu_items[ProjectMenu] = [ 'New', 'Open Project', 'Open Spectra', 'Save', 'Save As', 'Import', 'Close', 'Quit', 'Preferences', 'Validate', 'Backup', 'Archive', ] # Menus that area ctive in absence of a project #for ii in (0,1,2,7,13,15): for ii in ( 0, 1, 2, 7, ): self.fixedActiveMenus[(ProjectMenu, ii)] = True
class PulldownList(Frame): # if using indentation then should use list as stack only # in other words only delete or insert at end # There is a 1:1 correspondance between the ordered lists of texts and # list of objects # categories is the category name for each text/object # objects with the same category will go under a submenu # of that name def __init__(self, parent, callback=None, texts=None, objects=None, categories=None, colors=None, index=0, prefix='', indent='', initCallback=False, forceCallback=False, numbering=False, arrowLine='#602000', arrowFill='#B05848', labelColor='#501000', menuBg='#F0F0FF', sticky='w', docKey=None, tipText=None, categoriesLast=True, *args, **kw): Frame.__init__(self, parent, sticky=sticky, docKey=docKey, tipText=tipText, createToolTip=True, *args, **kw) self.callback = callback self.texts = texts or [] self.objects = objects or [] self.categories = categories or [] self.colors = colors or [] self.prefix = prefix self.indent = indent self.initCallback = initCallback self.numbering = numbering self.arrowLine = arrowLine self.arrowFill = arrowFill self.labelColor = labelColor self.active = True self.categoriesLast = categoriesLast # Current selection self.index = None self.object = NullText self.rows = [] self.bg = self.cget('bg') self.label = Label(self, foreground=labelColor) self.canvas = Canvas(self, width=12, height=12, background=self.bg) self.menu = Menu(self.canvas, tearoff=False, bg=menuBg, relief='solid', borderwidth=1, activeborderwidth=1) self.menu.images = [] # Photoimage has to remain referenced self.setup(self.texts, self.objects, index, self.colors, self.categories) self.label.bind( "<Button-1>", self._labelClick) self.menu.bind( "<Leave>", self._leave) self.canvas.bind("<Button-1>", self._canvasClick) self.canvas.bind("<Configure>", self._resizeCallback) self.grid_columnconfigure(0, weight=1) self.label.grid(row=0, column=0, sticky='w') self.canvas.grid(row=0, column=1, sticky='w', padx=2) # # Retrieval # def get(self): return (self.getText(),self.getObject()) def getSelected(self): return self.get() def getObject(self): return self._fetch(self.index, self.objects) def getText(self): return self._fetch(self.index, self.texts) def getSelectedIndex(self): return self.index # # Setting selected # def set(self, item, doCallback=False): # Works with an object or a text index = None if item in self.texts: index = list(self.texts).index(item) elif item in self.objects: index = list(self.objects).index(item) if index is not None: self.setIndex(index, doCallback=doCallback) def setSelected(self, item, doCallback=False): self.set(item, doCallback=doCallback) def setIndex(self, index, doCallback=False): self.index = index if self.objects: obj = self.objects[index] if obj is not self.object: self.object = obj if (doCallback or self.initCallback) and self.texts and self.callback: self.callback(obj) self._updateLabel() # # Bulk configuration # def clear(self): self.setup([], [], 0) def setup(self, texts, objects, index, colors=None, categories=None): self.texts = texts nTexts = len(texts) if not objects: objects = texts while len(objects) < nTexts: objects.append(None) self.objects = objects if colors is None: self.colors = [None] * nTexts else: while len(colors) < nTexts: colors.append(None) self.colors = colors if categories is None: self.categories = [None] * nTexts else: while len(categories) < nTexts: categories.append(None) self.categories = categories self._setMenuItems() self.setIndex(index or 0) # # In-place/minor configuration # def insert(self, index, text, object=None, color=None, category=None, select=False): index = max(0, min(len(self.texts),index)) self.texts.insert(index, text) self.objects.insert(index, object) self.colors.insert(index, color) self.categories.insert(index, category) self._setMenuItems() if select: self.setIndex(index) def append(self, text, object=None, color=None, category=None, select=False): self.insert(len(self.texts), text, object=None, color=None, category=None, select=False) def delete(self, index, howMany=1): if index < 0: return elif index >= len(self.texts): return end = min(index+howMany,len(self.texts)) self._clearMenu() del self.texts[index:end] del self.objects[index:end] del self.colors[index:end] del self.categories[index:end] self._setMenuItems() index = min(len(self.texts)-1,self.index) self.setIndex(index) def disable(self): self.label.config(foreground='#808080') self.active = False self._resizeCallback() def enable(self): self.label.config(foreground=self.labelColor) self.active = True self._resizeCallback() # # Internal methods # def _leave(self, event): # look for widget under event, if it is a Menu do not popdown # this often seems to throw exceptions deep in Tk so use try/except try: widget = self.menu.winfo_containing(event.x_root, event.y_root) if not isinstance(widget, Menu): self._popdown() except: """ The problem with the below approach is that when moving to a submenu the menu produces a leaving event which can easily be outside that menu, which causes an unintended popdown. """ x = event.x y = event.y x1 = self.menu.winfo_width() y1 = self.menu.winfo_height() if (x<0) or (y<0) or (x>=x1) or (y>=y1): self._popdown() def _resizeCallback(self, *event): if self.active: fill = self.arrowFill outline = self.arrowLine else: fill = '#808080' outline = '#404040' c = self.canvas w = c.winfo_width() -1 h = c.winfo_height() -1 c.delete('all') c.create_rectangle(0,0,10,2, fill=fill, outline=outline) c.create_polygon(0, 4, 0, 6, 5, 11, 10, 6, 10, 4, fill=fill, outline=outline) def _fetch(self, index, array): if index is None: return None if index < 0: index += len(array) if index < 0: return None elif index >= len(array): return None else: return array[index] def _setMenuItems(self): self._clearMenu() self.menu.images = [] # Clear photoimages if not self.texts and not self.menu.entrycget(1, 'label'): item = {'kind': 'command', 'label': NullText, 'command': None } self.menu.addMenuItem(item) self.rows = [0] return topList = [] categoryDict = {} catLast = self.categoriesLast catList = [] for i in range(len(self.texts)): text = self.texts[i] color = self.colors[i] category = self.categories[i] if type(category) is SET_TYPE: for altCat in category: if altCat is None: topList.append((i, text, color, None)) else: if categoryDict.get(altCat) is None: categoryDict[altCat] = [] if catLast: catList.append((None, altCat, None, altCat)) else: topList.append((None, altCat, None, altCat)) categoryDict[altCat].append((i, text, color)) elif category: if categoryDict.get(category) is None: categoryDict[category] = [] if catLast: catList.append((None, category, None, category)) else: topList.append((None, category, None, category)) categoryDict[category].append((i, text, color)) else: topList.append((i, text, color, None)) if catLast: catList.sort() topList += catList divider = False row = 0 for index, text, color, cat in topList: columnbreak = 0 if row and row % 36 == 0: columnbreak = 1 if cat: string = (self.indent * row) + self.prefix + text items = [] rowB = 0 for index2, text2, color2 in categoryDict.get(cat, []): columnbreakB = 0 if rowB and rowB % 36 == 0: columnbreakB = 1 if self.numbering: number = '%d%. ' % (index2+1) else: number = '' string2 = number + self.prefix + text2 command = lambda n=index2: self.setIndex(n, True) if color2: image = self._makeColorTile(color2) item2 = {'kind': 'command', 'accelerator': string2, 'command': command, 'image': image, 'columnbreak': columnbreakB} else: item2 = {'kind': 'command', 'label': string2, 'command': command, 'columnbreak': columnbreakB} items.append(item2) self.rows.append(row) rowB += 1 if catLast and not divider: divider = True self.menu.add_separator() item = {'kind':'cascade', 'label':string, 'submenu':items, 'columnbreak':columnbreak} else: if self.numbering: number = '%d%. ' % (index+1) else: number = '' string = (self.indent * row) + number + self.prefix + text command = lambda n=index: self.setIndex(n, True) if color: image = self._makeColorTile(color) item = {'kind': 'command', 'accelerator': string, 'command': command, 'image': image, 'columnbreak': columnbreak} else: item = {'kind': 'command', 'label': string, 'command': command, 'columnbreak': columnbreak} self.menu.addMenuItem(item) self.rows.append(row) row += 1 def _clearMenu(self): self.menu.delete(0, 'end') self.index = 0 self.rows = [] def _popdown(self, *event): self.menu.unpost() def _labelClick(self, event): if not self.active: return s = self.rows[self.index] #x = event.x_root - event.x + 2 + self.label.winfo_width() x = event.x_root - 2 y = event.y_root - event.y - max(0, s) * (self.label.winfo_height() + 1) self.menu.post(x, y) def _canvasClick(self, event): if not self.active: return s = self.rows[self.index] x = event.x_root - event.x + 2 y = event.y_root - event.y - max(0, s) * (self.label.winfo_height() + 1) self.menu.post(x, y) def _updateLabel(self): if self.texts: text = self.texts[self.index] or NullText else: text = NullText self.label.set(text=text) def _makeColorTile(self, color): image = Tkinter.PhotoImage() self.menu.images.append(image) if type(color) == type([]): colors = [ scaleColor(self.menu, c, 1.0) for c in color ] else: colors = [ scaleColor(self.menu, color, 1.0), ] cols = max(8, len(colors)) for x in range(cols): i = x % len(colors) c = colors[i] for y in range(16): image.put('{%s %s}' % (c,c), to=(2*x,y)) return image