コード例 #1
0
class AutoAssignIOCyclePopup(BasePopup):

    help_url = joinPath(getHelpUrlDir(), 'AutoAssignIOCycle.html')

    def __init__(self, parent, project):

        self.project = project
        self.guiParent = parent

        self.autoAssignFormat = AutoAssignFormat(project, guiParent=parent)

        self.format = 'autoAssign'

        self.chainStampSep = '-'

        self.dateTimeFlag = time.strftime("%Y%m%d.%H:%M")

        self.getPreviousDateTimeStamps()

        self.getChainsAndChainStamps()

        self.importFileName = self.exportFileName = None

        BasePopup.__init__(self,
                           parent=parent,
                           title="Project '%s': " % project.name +
                           'AutoAssign export/import cycle.',
                           modal=True,
                           transient=True)

    def getPreviousDateTimeStamps(self):

        self.dateTimeStamps = []
        appData = self.project.findAllApplicationData(application=self.format,
                                                      keyword=dateTimeStamp_kw)
        for appDatum in appData:
            self.dateTimeStamps.append(appDatum.value)

    def getChainsAndChainStamps(self):

        self.chains = []
        self.chainDateTimeStamps = []
        self.chainDateTimeStampDict = {}

        for molSys in self.project.sortedMolSystems():
            for chain in molSys.sortedChains():
                self.chains.append(chain)

                appData = chain.findFirstApplicationData(
                    application=self.format, keyword=dateTimeStamp_kw)
                if appData and appData.value in self.dateTimeStamps:
                    (tlist, tdict) = createSelection([chain])
                    cdtsTag = "%s%s%s" % (tlist[0], self.chainStampSep,
                                          appData.value)
                    self.chainDateTimeStamps.append(cdtsTag)
                    self.chainDateTimeStampDict[cdtsTag] = chain

    def body(self, master):

        self.geometry('600x400')

        #
        # Quick check
        #

        if not self.chains:
            showError("No chains",
                      "No chains available - cannot use export/import cycle.")
            self.destroy()

        #
        # Set it all up
        #

        columnspan = 2

        row = 0

        label = Label(master, text="AutoAssign export/import cycle.")
        label.grid(row=row, column=0, columnspan=columnspan, sticky=Tkinter.EW)

        row += 1

        separator = Separator(master, height=3)
        separator.setColor('black', bgColor='black')
        separator.grid(row=row,
                       column=0,
                       columnspan=columnspan,
                       sticky=Tkinter.EW)

        row += 1

        label = Label(
            master,
            fg='red',
            text=
            "Popup to export %s data from the CCPN data model,\nrun %s, then re-import the output."
            % (self.format, self.format))
        label.grid(row=row, column=0, columnspan=columnspan, sticky=Tkinter.EW)

        #
        # Make a break...
        #

        row += 1

        separator = Separator(master, height=3)
        separator.setColor('black', bgColor='black')
        separator.grid(row=row,
                       column=0,
                       columnspan=columnspan,
                       sticky=Tkinter.EW)

        #
        # Set up export file info
        #

        row += 1

        label = Label(master,
                      text="Export menu (using date/time label '%s')" %
                      self.dateTimeFlag)
        label.grid(row=row, column=0, columnspan=columnspan, sticky=Tkinter.EW)

        row += 1

        (chainList, self.chainDict) = createSelection(self.chains)

        label = Label(master, text="Select chain to export:")
        label.grid(row=row, column=0, sticky=Tkinter.W)

        self.chainSelect = PulldownMenu(master, entries=chainList)
        self.chainSelect.grid(row=row, column=1, sticky=Tkinter.W)

        row += 1

        label = Label(master, text="Export %s project file:" % self.format)
        label.grid(row=row, column=0, sticky=Tkinter.W)

        self.selectExportFileButton = Tkinter.Button(
            master,
            text='Select export file',
            command=(lambda: self.selectExportProjectFile()))
        self.selectExportFileButton.grid(row=row, column=1, sticky=Tkinter.W)

        row += 1

        self.exportButton = Tkinter.Button(master,
                                           text='Export',
                                           command=(lambda: self.doExport()))
        self.exportButton.grid(row=row,
                               column=0,
                               columnspan=columnspan,
                               sticky=Tkinter.EW)

        #
        # Make a break...
        #

        row += 1

        separator = Separator(master, height=3)
        separator.setColor('black', bgColor='black')
        separator.grid(row=row,
                       column=0,
                       columnspan=columnspan,
                       sticky=Tkinter.EW)

        #
        # Set up import file info
        #

        row += 1

        label = Label(master, text="Re-import menu")
        label.grid(row=row, column=0, columnspan=columnspan, sticky=Tkinter.EW)

        row += 1

        #
        # Select the right chain with the right date/time flag...
        #

        label = Label(master, text="Select chain with correct date/time flag:")
        label.grid(row=row, column=0, sticky=Tkinter.W)

        self.chainDateTimeSelect = PulldownMenu(
            master, entries=self.chainDateTimeStamps)
        self.chainDateTimeSelect.grid(row=row, column=1, sticky=Tkinter.W)
        # TODO UPDATE THIS WHEN EXPORT BUTTON PRESSED AND GETTING OK FROM EXPORT ITSELF!
        # Probably also need just a message if no importDateTimeStamp available...

        row += 1

        label = Label(master, text="Import %s output file:" % self.format)
        label.grid(row=row, column=0, sticky=Tkinter.W)

        self.selectImportFileButton = Tkinter.Button(
            master,
            text='Select import file',
            command=(lambda: self.selectImportShiftFile()))
        self.selectImportFileButton.grid(row=row, column=1, sticky=Tkinter.W)

        row += 1

        self.importButton = Tkinter.Button(master,
                                           text='Import',
                                           command=(lambda: self.doImport()))
        self.importButton.grid(row=row,
                               column=0,
                               columnspan=columnspan,
                               sticky=Tkinter.EW)

        #
        # Make a break...
        #

        row += 1

        separator = Separator(master, height=3)
        separator.setColor('black', bgColor='black')
        separator.grid(row=row,
                       column=0,
                       columnspan=columnspan,
                       sticky=Tkinter.EW)

        row += 1

        buttons = createDismissHelpButtonList(master,
                                              texts=[],
                                              commands=[],
                                              help_url=self.help_url)
        buttons.grid(row=row, columnspan=columnspan, column=0)

    def selectExportProjectFile(self):

        if self.exportFileName:
            fileName = self.exportFileName
        else:
            fileName = 'table.aat'

        popup = FormatFilePopup(
            self,
            file=fileName,
            component='project',
            format=self.format,
            title='Select name for %s project file to export.' % self.format)

        if popup.fileSelected:

            self.selectExportFileButton.config(text=popup.file)
            self.exportFileName = popup.file

    def selectImportShiftFile(self):

        if self.exportFileName:
            fileName = self.exportFileName
        else:
            fileName = 'autoAssign.out'

        popup = FormatFilePopup(
            self,
            file=fileName,
            component='shifts',
            format=self.format,
            title='Select name for %s output file to re-import.' % self.format)

        if popup.fileSelected:

            self.selectImportFileButton.config(text=popup.file)
            self.importFileName = popup.file

    def doExport(self):

        if self.exportFileName:
            chain = self.chainDict[self.chainSelect.getSelected()]
            returnValue = self.autoAssignFormat.writeProject(
                self.exportFileName, chain=chain, setTag=self.dateTimeFlag)
            if not returnValue:
                showError(
                    "No export file written",
                    "There were problems while exporting the %s project file."
                    % self.format)
            else:
                showInfo("File written", "File written successfully")
                cdtsTag = "%s-%s" % (self.chainSelect.getSelected(),
                                     self.dateTimeFlag)
                if not cdtsTag in self.chainDateTimeSelect.entries:
                    if "<None>" in self.chainDateTimeSelect.entries:
                        self.chainDateTimeSelect.entries.pop(
                            self.chainDateTimeSelect.entries.index("<None>"))
                    self.chainDateTimeSelect.replace(
                        [cdtsTag] + self.chainDateTimeSelect.entries)
                    self.chainDateTimeStampDict[cdtsTag] = chain

        else:
            showError(
                "No export file defined",
                "Please define a name for the %s project file to export to." %
                self.format)

    def doImport(self):

        if self.importFileName:
            cdtsTag = self.chainDateTimeSelect.getSelected()

            if not self.chainDateTimeStampDict.has_key(cdtsTag):
                showError(
                    "No import tag defined",
                    "No chain with date/time stamp defined - cannot re-import."
                )

            else:
                chain = self.chainDateTimeStampDict[cdtsTag]
                (chainText,
                 curDateTimeStamp) = cdtsTag.split(self.chainStampSep)

                #
                # Get relevant info from data model (not immediately necessary but as example)
                #
                # Note that could in principle use the normal peakNum tag, but dangerous in case
                # multiple exports were done... this is more laborious though.
                #

                peakLists = []

                rootPeakListTag = str((curDateTimeStamp, 'isRoot'))
                rootPeakNumToPeak = {}

                for nmrExp in self.project.currentNmrProject.sortedExperiments(
                ):
                    for ds in nmrExp.sortedDataSources():
                        for peakList in ds.sortedPeakLists():
                            appData = peakList.findAllApplicationData(
                                application=self.format,
                                keyword=dateTimeStamp_kw)
                            for appDatum in appData:
                                if appDatum and appDatum.value == curDateTimeStamp:
                                    peakLists.append(peakList)
                                    if peakList.findFirstApplicationData(
                                            application=self.format,
                                            keyword=ioCycleTag_kw,
                                            value=rootPeakListTag):
                                        for peak in peakList.sortedPeaks():
                                            appData = peak.findFirstApplicationData(
                                                application=self.format,
                                                keyword=ioCycleTag_kw)
                                            if appData:
                                                (curTag,
                                                 peakNum) = eval(appData.value)
                                                if curTag == curDateTimeStamp:
                                                    rootPeakNumToPeak[
                                                        peakNum] = peak

                #
                # Now get the actual chemical shift info from the AutoAssign output file
                #

                autoAssignChemShiftFile = AutoAssignChemShiftFile(
                    self.importFileName)
                autoAssignChemShiftFile.read()

                #
                # Set the mapping between the chain residues and the seqCode in the chem shift file
                #

                residues = list(chain.sortedResidues())
                seqCodeToResidue = {}
                for i in range(0, len(residues)):
                    residue = residues[i]
                    (seqCode,
                     code1Letter) = autoAssignChemShiftFile.seqCodes[i]
                    seqCode = returnInt(seqCode)
                    seqCodeToResidue[seqCode] = residue

                for rawChemShift in autoAssignChemShiftFile.chemShifts:

                    peak = None
                    residue = None
                    prevResidue = None

                    seqCode = rawChemShift.seqCode
                    atomName = rawChemShift.atomName
                    #allValues = rawChemShift.allValues
                    #rawChemShift.spinSystemId

                    if seqCodeToResidue.has_key(seqCode):
                        residue = seqCodeToResidue[seqCode]
                    else:
                        # THIS SHOULD NEVER HAPPEN!
                        print "  Error: no match for seqCode %d while re-importing project." % seqCode
                        continue

                    #
                    # Set info on residue/atom level
                    #

                    atom = self.findMatchingAtom(residue, atomName)
                    self.autoAssignFormat.setSeqAssignTag(
                        atom,
                        rawChemShift.value,
                        AppDataClass=Implementation.AppDataFloat)

                    #
                    # Set info on peak level
                    #

                    if rawChemShift.peakId:
                        (peakNum, rootName) = rawChemShift.peakId.split(
                            '.')  # TODO set this somewhere?
                        peakNum = returnInt(peakNum)
                        if rootPeakNumToPeak.has_key(peakNum):
                            peak = rootPeakNumToPeak[peakNum]
                            self.autoAssignFormat.setSeqAssignTag(
                                peak, str((chain.code, residue.seqId)))

        else:
            showError(
                "No import file defined",
                "Please define a name for the %s shift output file to import."
                % self.format)

    def findMatchingAtom(self, residue, atomName):

        atom = residue.findFirstAtom(name=atomName)
        if not atom:
            # Rough search but should be OK for bb atoms
            namingSystem = residue.chemCompVar.chemComp.findFirstNamingSystem(
                name='XPLOR')
            chemAtomSysName = findChemAtomSysName(namingSystem,
                                                  {'sysName': atomName})
            atom = residue.findFirstAtom(name=chemAtomSysName.atomName)

        return atom

    def apply(self):

        if not 0:
            showError("No root spectrum",
                      "Can't continue: need a root spectrum...")
            return False

        return True
コード例 #2
0
class RegionSelector(Frame):
    def __init__(self,
                 parent,
                 label='',
                 world_region=None,
                 view_region=None,
                 orient=Tkinter.HORIZONTAL,
                 allow_resize=True,
                 width=20,
                 callback=None,
                 borderwidth=1,
                 show_text=True,
                 text_color='#000000',
                 text_decimals=2,
                 units_scroll=0.1,
                 pages_scroll=1.0,
                 menu_entries=None,
                 menu_callback=None,
                 min_thickness=None,
                 *args,
                 **kw):

        self.menu_entries = menu_entries
        self.myCallback = callback

        Frame.__init__(self, parent, *args, **kw)

        self.text_decimals = text_decimals

        self.label = Label(self, text=label, width=4)

        self.menu = PulldownMenu(self,
                                 callback=menu_callback,
                                 entries=menu_entries)

        self.entry = FloatEntry(self,
                                width=6,
                                returnCallback=self.adjustScrollbar)

        self.region_scrollbar = RegionScrollbar(self,
                                                world_region=world_region,
                                                view_region=view_region,
                                                orient=orient,
                                                allow_resize=allow_resize,
                                                width=width,
                                                callback=self.doCallback,
                                                borderwidth=borderwidth,
                                                show_text=show_text,
                                                text_color=text_color,
                                                text_decimals=text_decimals,
                                                units_scroll=units_scroll,
                                                pages_scroll=pages_scroll,
                                                min_thickness=min_thickness)

        self.gridAll()

    def gridAll(self):

        col = 0
        if (self.menu_entries and len(self.menu_entries) > 1):
            self.menu.grid(row=0, column=col, sticky=Tkinter.EW)
            col = col + 1
        else:
            self.menu.grid_forget()

        self.label.grid(row=0, column=col, sticky=Tkinter.EW)
        col = col + 1
        self.entry.grid(row=0, column=col, sticky=Tkinter.EW)
        self.grid_columnconfigure(col, weight=0)
        col = col + 1
        self.region_scrollbar.grid(row=0, column=col, sticky=Tkinter.NSEW)
        self.grid_columnconfigure(col, weight=1)
        col = col + 1
        self.grid_columnconfigure(col, weight=0)

    def setMinThickness(self, min_thickness):

        self.region_scrollbar.setMinThickness(min_thickness)

    def setMenuEntries(self, menu_entries):

        self.menu_entries = menu_entries
        self.menu.replace(menu_entries)
        self.gridAll()

    def getMenuEntry(self):

        return self.menu.getSelected()

    def adjustScrollbar(self, *event):

        try:
            x = float(self.entry.get())
        except:
            showError('Entry error', 'Need to enter float in scrollbar box')
            self.setEntry()
            return

        (v0, v1) = self.region_scrollbar.view_region
        d = 0.5 * (v1 - v0)

        self.region_scrollbar.setViewRegion(x - d, x + d, do_callback=True)

    def doCallback(self, view_region):

        if (self.myCallback):
            self.myCallback(view_region)

        #print 'doCallback', view_region
        self.setEntry(view_region)

    def setEntry(self, view_region=None):

        if (not view_region):
            view_region = self.region_scrollbar.view_region
        (v0, v1) = view_region
        x = 0.5 * (v0 + v1)
        s = formatDecimals(x, decimals=self.text_decimals)
        self.entry.set(s)

    def __getattr__(self, name):

        # dispatch everything not defined by RegionSelector to scrollbar widget

        try:
            return getattr(self.__dict__['region_scrollbar'], name)
        except:
            raise AttributeError, "RegionSelector instance has no attribute '%s'" % name
コード例 #3
0
ファイル: HelpPopup.py プロジェクト: fenglb/ccpnmr2.4
class HelpFrame(Frame):
    def __init__(self,
                 parent,
                 width=70,
                 height=20,
                 xscroll=False,
                 yscroll=True,
                 *args,
                 **kw):

        apply(Frame.__init__, (self, parent) + args, kw)

        self.grid_columnconfigure(1, weight=1)

        row = 0
        texts = ('back', 'forward', 'load')
        commands = (self.prevUrl, self.nextUrl, self.loadUrl)
        self.buttons = ButtonList(self, texts=texts, commands=commands)
        self.buttons.grid(row=row, column=0)
        self.url_entry = Entry(self, returnCallback=self.loadUrl)
        self.url_entry.grid(row=row, column=1, columnspan=2, sticky=Tkinter.EW)
        row = row + 1

        frame = Frame(self)
        frame.grid(row=row, column=0, columnspan=3, sticky=Tkinter.W)
        self.createFindFrame(frame)
        row = row + 1

        self.grid_rowconfigure(row, weight=1)
        self.help_text = ScrolledHtml(self,
                                      width=width,
                                      height=height,
                                      xscroll=xscroll,
                                      yscroll=yscroll,
                                      startUrlCallback=self.startOpenUrl,
                                      endUrlCallback=self.endOpenUrl,
                                      enterLinkCallback=self.setLinkLabel,
                                      leaveLinkCallback=self.setLinkLabel)
        self.help_text.grid(row=row,
                            column=0,
                            columnspan=3,
                            sticky=Tkinter.NSEW)
        row = row + 1

        self.link_label = Label(self)
        self.link_label.grid(row=row, column=0, columnspan=2, sticky=Tkinter.W)
        button = memops.gui.Util.createDismissButton(self,
                                                     dismiss_cmd=self.close)
        button.grid(row=row, column=2)

        self.urls = []
        self.current = -1
        self.updateUrlList = True

        self.setButtonState()

    def close(self):

        if (self.popup):
            if (self.popup.modal):
                self.popup.do_grab()

        popup = self.parent
        while (not hasattr(popup, 'top')):
            popup = popup.parent
        popup.close()

    def createFindFrame(self, master):

        frame = Frame(master)
        frame.grid(row=0, column=0, sticky=Tkinter.W)

        arrow = ToggleArrow(frame, callback=self.toggleFindFrame)
        arrow.grid(row=0, column=0)
        button = Button(frame, text='find:', command=self.findPhrase)
        button.grid(row=0, column=1)
        self.find_entry = Entry(frame,
                                width=20,
                                returnCallback=self.findPhrase)
        self.find_entry.grid(row=0, column=2)

        self.find_frame = frame = Frame(master)

        entries = ('search forwards', 'search backwards')
        self.direction_buttons = PulldownMenu(frame, entries=entries)
        self.direction_buttons.grid(row=0, column=0, sticky=Tkinter.W, padx=5)

        self.forwards_entries = entries = ('wrap search', 'stop at end')
        self.backwards_entries = ('wrap search', 'stop at beginning')
        self.wrap_buttons = PulldownMenu(frame, entries=entries)
        self.wrap_buttons.grid(row=0, column=1, sticky=Tkinter.W, padx=5)

        self.direction_buttons.callback = self.setWrapText

        entries = ('case insensitive', 'case sensitive')
        self.case_buttons = PulldownMenu(frame, entries=entries)
        self.case_buttons.grid(row=0, column=2, sticky=Tkinter.W, padx=5)

        entries = ('literal search', 'regex search')
        self.pattern_buttons = PulldownMenu(frame, entries=entries)
        self.pattern_buttons.grid(row=0, column=3, sticky=Tkinter.W, padx=5)

        self.countVar = Tkinter.IntVar()

    def setWrapText(self, entry_ind, entry):

        if (entry_ind == 0):
            entries = self.forwards_entries
        else:
            entries = self.backwards_entries

        self.wrap_buttons.replace(
            entries, selected_index=self.wrap_buttons.getSelectedIndex())

    def findPhrase(self, *extra):

        phrase = self.find_entry.get()
        if (not phrase):
            showError('No find phrase', 'Enter phrase in entry box.')
            return

        if (self.direction_buttons.getSelectedIndex() == 0):
            forwards = 1
            backwards = 0
        else:
            forwards = 0
            backwards = 1

        if (self.case_buttons.getSelectedIndex() == 0):
            nocase = 1
        else:
            nocase = 0

        if (self.pattern_buttons.getSelectedIndex() == 0):
            exact = 1
            regexp = 0
        else:
            exact = 0
            regexp = 1

        start = self.help_text.tag_nextrange(Tkinter.SEL, '1.0')
        if (start):
            start = start[0]
            if (forwards):
                start = '%s+1c' % start
            else:
                start = '%s-1c' % start
        else:
            start = Tkinter.CURRENT

        if (self.wrap_buttons.getSelectedIndex() == 0):
            match = self.help_text.search(phrase,
                                          start,
                                          count=self.countVar,
                                          forwards=forwards,
                                          backwards=backwards,
                                          nocase=nocase,
                                          exact=exact,
                                          regexp=regexp)
        elif (forwards):
            match = self.help_text.search(phrase,
                                          start,
                                          count=self.countVar,
                                          forwards=forwards,
                                          backwards=backwards,
                                          nocase=nocase,
                                          exact=exact,
                                          regexp=regexp,
                                          stopindex=Tkinter.END)
        else:
            match = self.help_text.search(phrase,
                                          start,
                                          count=self.countVar,
                                          forwards=forwards,
                                          backwards=backwards,
                                          nocase=nocase,
                                          exact=exact,
                                          regexp=regexp,
                                          stopindex='1.0')

        if (match):
            self.help_text.see(match)
            self.help_text.setSelection(
                match, '%s+%dc' % (match, self.countVar.get()))
            self.help_text.tag_config(Tkinter.SEL, background='gray70')
        else:
            showInfo('No match', 'No match found for phrase.')

    def toggleFindFrame(self, isClosed):

        if (isClosed):
            self.find_frame.grid_forget()
        else:
            self.find_frame.grid(row=1, column=0, sticky=Tkinter.W)

    def setButtonState(self):

        n = len(self.urls)

        if ((n >= 2) and (self.current > 0)):
            state = Tkinter.NORMAL
        else:
            state = Tkinter.DISABLED
        self.buttons.buttons[0].config(state=state)

        if ((n >= 2) and (self.current < (n - 1))):
            state = Tkinter.NORMAL
        else:
            state = Tkinter.DISABLED
        self.buttons.buttons[1].config(state=state)

    def setLinkLabel(self, url=''):

        self.link_label.set(url)

    def loadUrl(self, *extra):

        url = self.url_entry.get()
        if (url):
            if (url.find(':') == -1):
                if (url[:2] != '//'):
                    url = '//' + url
                url = 'http:' + url
            self.showUrl(url, forceLoad=True)

    def newUrl(self, url):

        self.current = self.current + 1
        self.urls[self.current:] = [[url, 0.0]]
        self.setButtonState()
        #print 'newUrl', self.current, self.urls

    def prevUrl(self):

        if (self.current > 0):  # True if called via button
            (url, offset) = self.urls[self.current - 1]
            self.updateUrlList = False
            self.showUrl(url, offset=offset, allowModifyPath=False)
            self.updateUrlList = True
            self.current = self.current - 1
            self.setButtonState()
        #print 'prevUrl', self.current, self.urls

    def nextUrl(self):

        if (self.current < (len(self.urls) - 1)):  # True if called via button
            (url, offset) = self.urls[self.current + 1]
            self.updateUrlList = False
            self.showUrl(url, offset=offset, allowModifyPath=False)
            self.updateUrlList = True
            self.current = self.current + 1
            self.setButtonState()
        #print 'nextUrl', self.current, self.urls

    def showText(self, message, popup=None):

        self.popup = popup
        if (popup):
            self.parent.modal = popup.modal
        self.help_text.setState(Tkinter.NORMAL)  # so that can add text
        self.help_text.clear()  # clear existing text
        self.help_text.append(message)
        self.help_text.setState(Tkinter.DISABLED)  # make text read-only
        self.open()

    def startOpenUrl(self, yview):

        if (self.urls):
            self.urls[self.current][1] = yview[0]

    def endOpenUrl(self, url):

        #print 'endOpenUrl', url, self.updateUrlList
        self.url_entry.set(url)
        if (self.updateUrlList):
            self.newUrl(url)
        self.setLinkLabel()
        self.open()

    def showUrl(self,
                url,
                offset=None,
                forceLoad=False,
                allowModifyPath=True,
                popup=None):

        self.popup = popup
        if (popup):
            self.parent.modal = popup.modal
        self.help_text.openUrl(url,
                               forceLoad=forceLoad,
                               allowModifyPath=allowModifyPath)
        if (offset is not None):
            self.help_text.yview(Tkinter.MOVETO, str(offset))
コード例 #4
0
class GroupChemShiftsPopup(BasePopup):

    help_url = joinPath(getHelpUrlDir(), 'GroupChemShifts.html')

    def __init__(self, parent, project):

        #
        # Info for writing file...
        #

        self.groupText = {}

        #
        # Set up molSystem information
        #

        self.project = project

        self.molSysList = list(project.sortedMolSystems())
        self.molSystems = None

        self.molSysLabelList = []
        self.molSysLabelDict = {}
        self.molSysRelationDict = {}

        molSysLabel = 'All molSystems'

        if self.setChainLabels(molSysLabel, self.molSysList):
            self.molSysLabelList.append(molSysLabel)
            self.molSysLabelDict[molSysLabel] = self.molSysList

        for molSys in self.molSysList:
            molSysLabel = '%s' % molSys.code

            if self.setChainLabels(molSysLabel, [molSys]):
                self.molSysLabelList.append(molSysLabel)
                self.molSysLabelDict[molSysLabel] = [molSys]

        if not self.molSysLabelList:
            showWarning('Warning',
                        'No chemical shift lists available! Exiting...')
            return

        #
        # Some initializing...
        #

        self.chains = None
        self.shiftList = None
        self.shiftListLabel = None
        self.results = None

        # modal = true means that it won't continue unless this one returns value
        BasePopup.__init__(self,
                           parent=parent,
                           title="Project '%s': " % project.name +
                           'Group chemical shift values',
                           modal=False,
                           transient=True)

    def setChainLabels(self, molSysLabel, molSysList):

        #
        # Set up chain information
        #

        chainLabelList = []
        chainLabelDict = {}
        chainLabelShiftListDict = {}

        self.molSysRelationDict[molSysLabel] = [
            chainLabelList, chainLabelDict, chainLabelShiftListDict
        ]

        chains = []

        for molSys in molSysList:
            chains.extend(list(molSys.sortedChains()))

        chainLabel = 'All chains'

        if self.setShiftListLabels(chainLabel, chains,
                                   self.molSysRelationDict[molSysLabel][2]):
            self.molSysRelationDict[molSysLabel][0].append(chainLabel)
            self.molSysRelationDict[molSysLabel][1][chainLabel] = chains

        for chain in chains:
            chainLabel = "'%s' (mol. '%s')" % (chain.code, chain.molecule.name)

            if self.setShiftListLabels(
                    chainLabel, [chain],
                    self.molSysRelationDict[molSysLabel][2]):
                self.molSysRelationDict[molSysLabel][0].append(chainLabel)
                self.molSysRelationDict[molSysLabel][1][chainLabel] = [chain]

        return self.molSysRelationDict[molSysLabel][0]

    def setShiftListLabels(self, chainLabel, chains, chainLabelShiftListDict):

        #
        # Set up chemical shift list information (slooooww so done at start)
        #

        shiftLists = []
        shiftListCount = {}
        resonanceTracker = []
        shiftListLabels = []
        shiftListLabelsDict = {}
        atomCount = 0

        for chain in chains:
            for residue in chain.residues:
                for atom in residue.atoms:
                    atomCount += 1
                    if atom.atomSet:
                        for resonanceSet in atom.atomSet.resonanceSets:
                            for resonance in resonanceSet.resonances:

                                if resonance in resonanceTracker:
                                    continue

                                resonanceTracker.append(resonance)

                                for shift in resonance.shifts:

                                    if shift.parentList not in shiftLists:
                                        shiftLists.append(shift.parentList)
                                        shiftListCount[shift.parentList] = 0

                                    shiftListCount[shift.parentList] += 1

        for shiftList in shiftLists:
            percentage = (shiftListCount[shift.parentList] * 100.0) / atomCount
            shiftListLabel = "%d:%s (%.1f%%)" % (shiftList.serial,
                                                 shiftList.name, percentage)
            shiftListLabels.append(shiftListLabel)
            shiftListLabelsDict[shiftListLabel] = shiftList

        chainLabelShiftListDict[chainLabel] = [
            shiftListLabels, shiftListLabelsDict
        ]

        return shiftListLabels

    def body(self, master):

        #
        # Setup header
        #

        row = 0
        self.columnSpan = 3

        label = Label(
            master,
            text=
            'Select molecular system, chains and chemical shift list to be analyzed:'
        )
        label.grid(row=row,
                   column=0,
                   columnspan=self.columnSpan,
                   sticky=Tkinter.EW)

        row += 1

        self.molSysSelect = PulldownMenu(master,
                                         entries=self.molSysLabelList,
                                         callback=self.setupChains)
        self.molSysSelect.grid(row=row, column=0, sticky=Tkinter.W)

        self.chainSelect = PulldownMenu(master,
                                        entries=self.chainLabelList,
                                        callback=self.setupShiftList)
        self.chainSelect.grid(row=row, column=1, sticky=Tkinter.W)

        self.shiftListSelect = PulldownMenu(master,
                                            entries=self.shiftListLabels,
                                            callback=self.setupPercentageFrame,
                                            do_initial_callback=False)
        self.shiftListSelect.grid(row=row, column=2, sticky=Tkinter.W)

        row += 1

        separator = Separator(master, height=3)
        separator.setColor('black', bgColor='black')
        separator.grid(row=row, columnspan=self.columnSpan, sticky=Tkinter.EW)

        row += 1

        master.grid_rowconfigure(row, weight=1)
        for i in range(self.columnSpan):
            master.grid_columnconfigure(i, weight=1)

        self.percentageFrame = ScrolledFrame(master,
                                             height=180,
                                             doExtraConfig=False)
        self.percentageFrame.grid(row=row,
                                  columnspan=self.columnSpan,
                                  sticky=Tkinter.NSEW)
        self.percentageFrameRow = row
        self.percentageFrameMaster = master

        row += 1

        separator = Separator(master, height=3)
        separator.setColor('black', bgColor='black')
        separator.grid(row=row, columnspan=self.columnSpan, sticky=Tkinter.EW)

        row += 1

        master.grid_rowconfigure(row, weight=1)
        self.resultsFrame = ScrolledFrame(master,
                                          height=180,
                                          doExtraConfig=False)
        self.resultsFrame.grid(row=row,
                               columnspan=self.columnSpan,
                               sticky=Tkinter.NSEW)
        self.resultsFrameRow = row
        self.resultsFrameMaster = master

        row += 1

        separator = Separator(master, height=3)
        separator.setColor('black', bgColor='black')
        separator.grid(row=row, columnspan=self.columnSpan, sticky=Tkinter.EW)

        row += 1

        texts = ['Recalculate', 'Write to file']
        commands = [
            self.recalc, self.writeFile
        ]  # This calls 'ok' in BasePopup, this then calls 'apply' in here
        buttons = createDismissHelpButtonList(master,
                                              texts=texts,
                                              commands=commands,
                                              dismiss_text='Exit',
                                              help_url=self.help_url)
        buttons.grid(row=row, columnspan=self.columnSpan, sticky=Tkinter.EW)

        self.shiftListSelect.callback(0, self.shiftListLabels[0])

    def setupChains(self, listIndex, molSysLabel):

        molSystems = self.molSysLabelDict[molSysLabel]

        if self.molSystems == molSystems:
            return

        self.molSystems = molSystems

        self.chainLabelList = self.molSysRelationDict[molSysLabel][0]
        self.chainLabelDict = self.molSysRelationDict[molSysLabel][1]
        self.chainLabelShiftListDict = self.molSysRelationDict[molSysLabel][2]

        if hasattr(self, 'chainSelect'):
            self.chains = None
            self.chainSelect.callback(0, self.chainLabelList[0])

    def setupShiftList(self, listIndex, chainLabel):

        chains = self.chainLabelDict[chainLabel]

        if self.chains == chains:
            return

        self.chains = chains

        #
        # Reset the pulldown menu
        #

        if hasattr(self, 'chainSelect'):
            self.chainSelect.replace(self.chainLabelList)

        #
        # Set up chemical shift lists
        #

        self.shiftListLabels = self.chainLabelShiftListDict[chainLabel][0]
        self.shiftListLabelsDict = self.chainLabelShiftListDict[chainLabel][1]

        if hasattr(self, 'percentageFrame'):
            self.shiftList = None
            self.shiftListLabel = None
            self.setupPercentageFrame(0, self.shiftListLabels[0])

    def setupPercentageFrame(self, listIndex, shiftListLabel):

        if self.shiftListLabel == shiftListLabel:
            return

        self.shiftList = self.shiftListLabelsDict[shiftListLabel]
        self.shiftListLabel = shiftListLabel

        #
        # Reset chemical shift groups...
        #

        self.shiftGroupsList = []

        for i in range(1, 10):
            shiftGroupLabel = 'Group_%s' % i
            self.shiftGroupsList.append(shiftGroupLabel)

        #
        # Reset the pulldown menu
        #

        if hasattr(self, 'shiftListSelect'):
            if hasattr(self.shiftListSelect,
                       'entries') and tuple(self.shiftListLabels) != tuple(
                           self.shiftListSelect.entries):  # HACK
                # otherwise can get infinite recursion but even with above could
                # if no self.shiftListLabels because of "None" entry in PulldownMenu
                self.shiftListSelect.replace(self.shiftListLabels)
            self.shiftListSelect.setSelected(shiftListLabel)

        #
        # Reset frame...
        #

        self.percentageFrame.destroy()
        self.percentageFrame = ScrolledFrame(self.percentageFrameMaster,
                                             height=180,
                                             doExtraConfig=False)
        self.percentageFrame.grid(row=self.percentageFrameRow,
                                  columnspan=self.columnSpan,
                                  sticky=Tkinter.NSEW)

        #
        # Recalculate results
        #

        self.results = makeChemShiftSelections(self.parent, self.chains,
                                               self.shiftList)
        resultKeys = self.results.keys()
        resultKeys.sort()

        #
        # Set up the information
        #

        frameRow = 0
        frame = self.percentageFrame.frame

        self.shiftGroupObjects = {}

        for resultKey in resultKeys:

            allAtoms = self.results[resultKey][0]
            shiftAtoms = self.results[resultKey][1]

            percentage = shiftAtoms * 100.0 / allAtoms

            label = Label(frame, text=resultKey)
            label.grid(row=frameRow, column=0, sticky=Tkinter.W)

            label = Label(frame, text="%.1f%%" % percentage)
            label.grid(row=frameRow, column=1, sticky=Tkinter.W)

            label = Label(frame, text="(%d/%d)" % (shiftAtoms, allAtoms))
            label.grid(row=frameRow, column=2, sticky=Tkinter.W)

            self.shiftGroupObjects[resultKey] = PulldownMenu(
                frame, entries=self.shiftGroupsList)
            self.shiftGroupObjects[resultKey].grid(row=frameRow,
                                                   column=3,
                                                   sticky=Tkinter.E)

            frameRow += 1

        return True

    def recalc(self):

        groups = {}
        groupsInfo = {}

        for resultKey in self.shiftGroupObjects:
            group = self.shiftGroupObjects[resultKey].getSelected()

            if not groups.has_key(group):
                groups[group] = []
                groupsInfo[group] = [0, 0]

            (allAtoms, shiftAtoms) = self.results[resultKey]
            groupsInfo[group][0] += allAtoms
            groupsInfo[group][1] += shiftAtoms

            groups[group].append(resultKey)

        #
        # Reset frame...
        #

        self.resultsFrame.destroy()
        self.resultsFrame = ScrolledFrame(self.resultsFrameMaster,
                                          height=180,
                                          doExtraConfig=False)
        self.resultsFrame.grid(row=self.resultsFrameRow,
                               columnspan=self.columnSpan,
                               sticky=Tkinter.NSEW)

        #
        # Set info in lower frame
        #

        frameRow = 0
        frame = self.resultsFrame.frame

        groupKeys = groups.keys()
        groupKeys.sort()

        self.groupText = {}

        for group in groupKeys:

            percentage = groupsInfo[group][1] * 100.0 / groupsInfo[group][0]

            label = Label(frame, text=group)
            label.grid(row=frameRow, column=0, sticky=Tkinter.W)

            label = Label(frame, text=groups[group][0])
            label.grid(row=frameRow, column=1, sticky=Tkinter.W)

            label = Label(frame, text="%.1f%%" % percentage)
            label.grid(row=frameRow, column=2, sticky=Tkinter.W)

            label = Label(frame,
                          text="(%d/%d)" %
                          (groupsInfo[group][1], groupsInfo[group][0]))
            label.grid(row=frameRow, column=3, sticky=Tkinter.W)

            self.groupText[group] = "%-10s %-20s %8.1f%% (%d/%d)%s" % (
                group, groups[group][0], percentage, groupsInfo[group][1],
                groupsInfo[group][0], newline)

            frameRow += 1

            for otherResultKey in groups[group][1:]:
                label = Label(frame, text=otherResultKey)
                label.grid(row=frameRow, column=2, sticky=Tkinter.W)

                self.groupText[group] += "%-10s %-20s%s" % ('', otherResultKey,
                                                            newline)

                frameRow += 1

        return True

    def writeFile(self):

        filePopup = FormatFilePopup(self, component='text')

        if filePopup.fileSelected:

            groupKeys = self.groupText.keys()
            groupKeys.sort()

            fout = open(filePopup.file, 'w')
            fout.write("Project: %s" % self.project.name + newline)
            fout.write("Molsystems: %s" % self.molSysSelect.getSelected() +
                       newline)
            fout.write("Chains: %s" % self.chainSelect.getSelected() + newline)
            fout.write("Shiftlist: %s" % self.shiftListSelect.getSelected() +
                       newline * 2)

            for group in groupKeys:

                fout.write(self.groupText[group])

            print "Wrote file %s..." % filePopup.file

    def apply(self):

        return True