source: trunk/GSASII.py @ 1232

Last change on this file since 1232 was 1232, checked in by vondreele, 8 years ago

add copy substances
fix azimuth offset in polarization calc
make sasd come out as semilog plot
fix limits in log plots

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 153.2 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2014-02-26 18:41:28 +0000 (Wed, 26 Feb 2014) $
6# $Author: vondreele $
7# $Revision: 1232 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1232 2014-02-26 18:41:28Z vondreele $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. Please restart GSAS-II')
59    print('*******************************************************')         
60    sys.exit()
61
62# load the GSAS routines
63import GSASIIpath
64GSASIIpath.SetVersionNumber("$Revision: 1232 $")
65import GSASIIIO as G2IO
66import GSASIIgrid as G2gd
67import GSASIIplot as G2plt
68import GSASIIpwd as G2pwd
69import GSASIIpwdGUI as G2pdG
70import GSASIIspc as G2spc
71import GSASIIstrMain as G2stMn
72import GSASIIstrIO as G2stIO
73import GSASIImapvars as G2mv
74import GSASIIobj as G2obj
75import GSASIIlattice as G2lat
76
77#wx inspector - use as needed
78wxInspector = False
79
80# print versions
81print "Python module versions loaded:"
82print "python:     ",sys.version[:5]
83print "wxpython:   ",wx.__version__
84print "matplotlib: ",mpl.__version__
85print "numpy:      ",np.__version__
86print "scipy:      ",sp.__version__
87print "OpenGL:     ",ogl.__version__
88try:
89    import mkl
90    print "Max threads ",mkl.get_max_threads()
91except:
92    pass
93#    print "MKL module not present"
94__version__ = '0.2.0'
95G2gd.__version__ = __version__
96print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
97
98# useful degree trig functions
99sind = lambda x: math.sin(x*math.pi/180.)
100cosd = lambda x: math.cos(x*math.pi/180.)
101tand = lambda x: math.tan(x*math.pi/180.)
102asind = lambda x: 180.*math.asin(x)/math.pi
103acosd = lambda x: 180.*math.acos(x)/math.pi
104atan2d = lambda x,y: 180.*math.atan2(y,x)/math.pi
105
106def create(parent):
107    return GSASII(parent)
108
109class GSASII(wx.Frame):
110    '''Define the main GSAS-II frame and its associated menu items
111    '''
112    def _Add_FileMenuItems(self, parent):
113        item = parent.Append(
114            help='Open a GSAS-II project file (*.gpx)', id=wx.ID_ANY,
115            kind=wx.ITEM_NORMAL,text='&Open project...')
116        self.Bind(wx.EVT_MENU, self.OnFileOpen, id=item.GetId())
117        item = parent.Append(
118            help='Save project under current name', id=wx.ID_ANY,
119            kind=wx.ITEM_NORMAL,text='&Save project')
120        self.Bind(wx.EVT_MENU, self.OnFileSave, id=item.GetId())
121        item = parent.Append(
122            help='Save current project to new file', id=wx.ID_ANY,
123            kind=wx.ITEM_NORMAL,text='Save project as...')
124        self.Bind(wx.EVT_MENU, self.OnFileSaveas, id=item.GetId())
125        item = parent.Append(
126            help='Create empty new project, saving current is optional', id=wx.ID_ANY,
127            kind=wx.ITEM_NORMAL,text='&New project')
128        self.Bind(wx.EVT_MENU, self.OnFileClose, id=item.GetId())
129        item = parent.Append(
130            help='Exit from GSAS-II', id=wx.ID_ANY,
131            kind=wx.ITEM_NORMAL,text='&Exit')
132        self.Bind(wx.EVT_MENU, self.OnFileExit, id=item.GetId())
133       
134    def _Add_DataMenuItems(self,parent):
135        item = parent.Append(
136            help='',id=wx.ID_ANY,
137            kind=wx.ITEM_NORMAL,
138            text='Read image data...')
139        self.Bind(wx.EVT_MENU, self.OnImageRead, id=item.GetId())
140        item = parent.Append(
141            help='',id=wx.ID_ANY,
142            kind=wx.ITEM_NORMAL,
143            text='Read Powder Pattern Peaks...')
144        self.Bind(wx.EVT_MENU, self.OnReadPowderPeaks, id=item.GetId())
145        item = parent.Append(
146            help='',id=wx.ID_ANY,
147            kind=wx.ITEM_NORMAL,
148            text='Sum powder data')
149        self.Bind(wx.EVT_MENU, self.OnPwdrSum, id=item.GetId())
150        item = parent.Append(
151            help='',id=wx.ID_ANY,
152            kind=wx.ITEM_NORMAL,
153            text='Sum image data')
154        self.Bind(wx.EVT_MENU, self.OnImageSum, id=item.GetId())
155        item = parent.Append(
156            help='',id=wx.ID_ANY,
157            kind=wx.ITEM_NORMAL,
158            text='Add phase')
159        self.Bind(wx.EVT_MENU, self.OnAddPhase, id=item.GetId())
160        item = parent.Append(
161            help='',id=wx.ID_ANY,
162            kind=wx.ITEM_NORMAL,
163            text='Delete phase')
164        self.Bind(wx.EVT_MENU, self.OnDeletePhase, id=item.GetId())
165        item = parent.Append(
166            help='',id=wx.ID_ANY,
167            kind=wx.ITEM_NORMAL,
168            text='Rename data') 
169        self.Bind(wx.EVT_MENU, self.OnRenameData, id=item.GetId())
170        item = parent.Append(
171            help='',id=wx.ID_ANY,
172            kind=wx.ITEM_NORMAL,
173            text='Delete data')
174        self.Bind(wx.EVT_MENU, self.OnDataDelete, id=item.GetId())
175               
176    def _Add_CalculateMenuItems(self,parent):
177        item = parent.Append(help='Make new PDFs from selected powder patterns', 
178            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='Make new PDFs')
179        self.MakePDF.append(item)
180#        item.Enable(False)
181        self.Bind(wx.EVT_MENU, self.OnMakePDFs, id=item.GetId())
182       
183        item = parent.Append(help='View least squares parameters', 
184            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='&View LS parms')
185        self.Bind(wx.EVT_MENU, self.ShowLSParms, id=item.GetId())
186       
187        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
188            text='&Refine')
189        if len(self.Refine): # extend state for new menus to match main (on mac)
190            state = self.Refine[0].IsEnabled()
191        else:
192            state = False
193        item.Enable(state)
194        self.Refine.append(item)
195        self.Bind(wx.EVT_MENU, self.OnRefine, id=item.GetId())
196       
197        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
198            text='Sequential refine')
199        if len(self.SeqRefine): # extend state for new menus to match main (on mac)
200            state = self.SeqRefine[0].IsEnabled()
201        else:
202            state = False
203        item.Enable(state)
204        self.SeqRefine.append(item)
205        self.Bind(wx.EVT_MENU, self.OnSeqRefine, id=item.GetId())
206       
207    def _init_Imports(self):
208        '''import all the G2phase*.py & G2sfact*.py & G2pwd*.py files that
209        are found in the path
210        '''
211
212        self.ImportPhaseReaderlist = []
213        self._init_Import_routines('phase',self.ImportPhaseReaderlist,'Phase')
214        self.ImportSfactReaderlist = []
215        self._init_Import_routines('sfact',self.ImportSfactReaderlist,'Struct_Factor')
216        self.ImportPowderReaderlist = []
217        self._init_Import_routines('pwd',self.ImportPowderReaderlist,'Powder_Data')
218        self.ImportSmallAngleReaderlist = []
219        self._init_Import_routines('sad',self.ImportSmallAngleReaderlist,'SmallAngle_Data')
220        self.ImportMenuId = {}
221
222    def _init_Import_routines(self,prefix,readerlist,errprefix):
223        '''import all the import readers matching the prefix
224        '''
225        #path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file
226        #pathlist = sys.path[:]
227        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
228        #path2GSAS2 = os.path.join(
229        #    os.path.dirname(os.path.realpath(__file__)), # location of this file
230        #    'imports')
231        pathlist = sys.path[:]
232        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
233
234        filelist = []
235        for path in pathlist:
236            for filename in glob.iglob(os.path.join(
237                path,
238                "G2"+prefix+"*.py")):
239                filelist.append(filename)   
240                #print 'debug: found',filename
241        filelist = sorted(list(set(filelist))) # remove duplicates
242        for filename in filelist:
243            path,rootname = os.path.split(filename)
244            pkg = os.path.splitext(rootname)[0]
245            try:
246                fp = None
247                fp, fppath,desc = imp.find_module(pkg,[path,])
248                pkg = imp.load_module(pkg,fp,fppath,desc)
249                for clss in inspect.getmembers(pkg): # find classes defined in package
250                    if clss[0].startswith('_'): continue
251                    if inspect.isclass(clss[1]):
252                        # check if we have the required methods
253                        for m in 'Reader','ExtensionValidator','ContentsValidator':
254                            if not hasattr(clss[1],m): break
255                            if not callable(getattr(clss[1],m)): break
256                        else:
257                            reader = clss[1]() # create an import instance
258                            readerlist.append(reader)
259            except AttributeError:
260                print 'Import_'+errprefix+': Attribute Error '+str(filename)
261                pass
262            except ImportError:
263                print 'Import_'+errprefix+': Error importing file '+str(filename)
264                pass
265            if fp: fp.close()
266
267    def OnImportGeneric(self,reader,readerlist,label,multiple=False,usedRanIdList=[]):
268        '''Used to import Phases, powder dataset or single
269        crystal datasets (structure factor tables) using reader objects
270        subclassed from :class:`GSASIIIO.ImportPhase`, :class:`GSASIIIO.ImportStructFactor`
271        or :class:`GSASIIIO.ImportPowderData`. If a reader is specified, only
272        that will be attempted, but if no reader is specified, every one
273        that is potentially compatible (by file extension) will
274        be tried on the selected file(s).
275
276        :param readerobject reader: This will be a reference to
277          a particular object to be used to read a file or None,
278          if every appropriate reader should be used.
279
280        :param list readerlist: a list of reader objects appropriate for
281          the current read attempt. At present, this will be either
282          self.ImportPhaseReaderlist, self.ImportSfactReaderlist or
283          self.ImportPowderReaderlist (defined in _init_Imports from
284          the files found in the path), but in theory this list could
285          be tailored. Used only when reader is None.
286
287        :param str label: string to place on the open file dialog:
288          Open `label` input file
289
290        :param bool multiple: True if multiple files can be selected
291          in the file dialog. False is default. At present True is used
292          only for reading of powder data.
293
294        :param list usedRanIdList: an optional list of random Ids that
295          have been used and should not be reused
296
297        :returns: a list of reader objects (rd_list) that were able
298          to read the specified file(s). This list may be empty.
299        '''
300        self.lastimport = ''
301        self.zipfile = None
302        singlereader = True
303        if reader is None:
304            singlereader = False
305            multiple = False
306            #print "use all formats"
307            choices = "any file (*.*)|*.*"
308            choices += "|zip archive (.zip)|*.zip"
309            extdict = {}
310            # compile a list of allowed extensions
311            for rd in readerlist:
312                fmt = rd.formatName
313                for extn in rd.extensionlist:
314                    if not extdict.get(extn): extdict[extn] = []
315                    extdict[extn] += [fmt,]
316            for extn in sorted(extdict.keys(),cmp=lambda x,y: cmp(x.lower(), y.lower())):
317                fmt = ''
318                for f in extdict[extn]:
319                    if fmt != "": fmt += ', '
320                    fmt += f
321                choices += "|" + fmt + " file (*" + extn + ")|*" + extn
322        else:
323            readerlist = [reader,]
324            # compile a list of allowed extensions
325            choices = reader.formatName + " file ("
326            w = ""
327            for extn in reader.extensionlist:
328                if w != "": w += ";"
329                w += "*" + extn
330            choices += w + ")|" + w
331            choices += "|zip archive (.zip)|*.zip"
332            if not reader.strictExtension:
333                choices += "|any file (*.*)|*.*"
334        # get the file(s)
335        if multiple:
336            mode = style=wx.OPEN | wx.CHANGE_DIR | wx.MULTIPLE
337        else:
338            mode = style=wx.OPEN | wx.CHANGE_DIR
339        dlg = wx.FileDialog(self, message="Choose "+label+" input file",
340            defaultFile="",wildcard=choices, style=mode)
341        try:
342            if dlg.ShowModal() == wx.ID_OK:
343                if multiple:
344                    filelist = dlg.GetPaths()
345                    if len(filelist) == 0: return []
346                else:
347                    filename = dlg.GetPath()
348                    filelist = [filename,]
349                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
350            else: # cancel was pressed
351                return []
352        finally:
353            dlg.Destroy()
354        rd_list = []
355        filelist1 = []
356        for filename in filelist:
357            # is this a zip file?
358            if os.path.splitext(filename)[1].lower() == '.zip':
359                extractedfiles = G2IO.ExtractFileFromZip(
360                    filename,parent=self,
361                    multipleselect=True)
362                if extractedfiles is None: continue # error or Cancel
363                if extractedfiles != filename:
364                    self.zipfile = filename # save zip name
365                    filelist1 += extractedfiles
366                    continue
367            filelist1.append(filename)
368        filelist = filelist1
369        for filename in filelist:
370            # is this a zip file?
371            if os.path.splitext(filename)[1].lower() == '.zip':
372                extractedfile = G2IO.ExtractFileFromZip(filename,parent=self)
373                if extractedfile is None: continue # error or Cancel
374                if extractedfile != filename:
375                    filename,self.zipfile = extractedfile,filename # now use the file that was created
376            # set what formats are compatible with this file
377            primaryReaders = []
378            secondaryReaders = []
379            for rd in readerlist:
380                flag = rd.ExtensionValidator(filename)
381                if flag is None: 
382                    secondaryReaders.append(rd)
383                elif flag:
384                    primaryReaders.append(rd)
385            if len(secondaryReaders) + len(primaryReaders) == 0:
386                self.ErrorDialog('No Format','No matching format for file '+filename)
387                return []
388
389            fp = None
390            msg = ''
391            try:
392                fp = open(filename,'Ur')
393                if len(filelist) == 1:
394                    # confirm we have the right file
395                    rdmsg = 'File '+str(filename)+' begins:\n\n'
396                    for i in range(3):
397                        rdmsg += fp.readline()
398                    rdmsg += '\n\nDo you want to read this file?'
399                    if not all([ord(c) < 128 and ord(c) != 0 for c in rdmsg]): # show only if ASCII
400                        rdmsg = 'File '+str(
401                            filename)+' is a binary file. Do you want to read this file?'
402                    result = wx.ID_NO
403                    # it would be better to use something that
404                    # would resize better, but this will do for now
405                    dlg = wx.MessageDialog(
406                        self, rdmsg,
407                        'Is this the file you want?', 
408                        wx.YES_NO | wx.ICON_QUESTION,
409                        )
410                    dlg.SetSize((700,300)) # does not resize on Mac
411                    try:
412                        result = dlg.ShowModal()
413                    finally:
414                        dlg.Destroy()
415                    if result == wx.ID_NO: return []
416                           
417                self.lastimport = filename
418                # try the file first with Readers that specify the
419                # file's extension and later with ones that merely allow it
420                errorReport = ''
421                for rd in primaryReaders+secondaryReaders:
422                    rd.ReInitialize() # purge anything from a previous read
423                    fp.seek(0)  # rewind
424                    rd.errors = "" # clear out any old errors
425                    if not rd.ContentsValidator(fp): # rejected on cursory check
426                        errorReport += "\n  "+rd.formatName + ' validator error'
427                        if rd.errors: 
428                            errorReport += ': '+rd.errors
429                        continue 
430                    repeat = True
431                    rdbuffer = {} # create temporary storage for file reader
432                    block = 0
433                    while repeat: # loop if the reader asks for another pass on the file
434                        block += 1
435                        repeat = False
436                        fp.seek(0)  # rewind
437                        rd.objname = os.path.basename(filename)
438                        flag = False
439                        try:
440                            flag = rd.Reader(filename,fp,self,
441                                             buffer=rdbuffer,
442                                             blocknum=block,
443                                             usedRanIdList=usedRanIdList,
444                                             )
445                        except rd.ImportException as detail:
446                            rd.errors += "\n  Read exception: "+str(detail)
447                        except Exception as detail:
448                            rd.errors += "\n  Unhandled read exception: "+str(detail)
449                            rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
450                        if flag: # this read succeeded
451                            rd_list.append(copy.deepcopy(rd)) # save the result before it is written over
452                            if rd.repeat:
453                                repeat = True
454                            continue
455                        errorReport += '\n'+rd.formatName + ' read error'
456                        if rd.errors:
457                            errorReport += ': '+rd.errors
458                    if rd_list: # read succeeded, was there a warning or any errors?
459                        if rd.warnings:
460                            self.ErrorDialog('Read Warning','The '+ rd.formatName+
461                                             ' reader reported a warning message:\n\n'+
462                                             rd.warnings)
463                        break # success in reading, try no further
464                else:
465                    if singlereader:
466                        self.ErrorDialog('Read Error','The '+ rd.formatName+
467                                         ' reader was not able to read file '+filename+msg+
468                                         '\n\nError message(s):\n'+errorReport
469                                         )
470                    else:
471                        self.ErrorDialog('Read Error','No reader was able to read file '+filename+msg+
472                                         '\n\nError messages:\n'+errorReport
473                                         )
474            except:
475                import traceback
476                print traceback.format_exc()
477                self.ErrorDialog('Open Error','Unexpected error trying to open or read file '+filename)
478            if fp: fp.close()
479        return rd_list
480
481    def _Add_ImportMenu_Phase(self,parent):
482        '''configure the Import Phase menus accord to the readers found in _init_Imports
483        '''
484        submenu = wx.Menu()
485        item = parent.AppendMenu(wx.ID_ANY, 'Phase',
486            submenu, help='Import phase data')
487        for reader in self.ImportPhaseReaderlist:
488            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
489                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
490            self.ImportMenuId[item.GetId()] = reader
491            self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
492        item = submenu.Append(wx.ID_ANY,
493                              help='Import phase data, use file to try to determine format',
494                              kind=wx.ITEM_NORMAL,
495                              text='guess format from file')
496        self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
497
498    def OnImportPhase(self,event):
499        '''Called in response to an Import/Phase/... menu item
500        to read phase information.
501        dict self.ImportMenuId is used to look up the specific
502        reader item associated with the menu item, which will be
503        None for the last menu item, which is the "guess" option
504        where all appropriate formats will be tried.
505        '''
506        # look up which format was requested
507        reqrdr = self.ImportMenuId.get(event.GetId())
508       
509        # make a list of phase names, ranId's and the histograms used in those phases
510        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
511        phaseNameList = usedHistograms.keys() # phase names in use
512        usedHKLFhists = [] # used single-crystal histograms
513        for p in usedHistograms:
514            for h in usedHistograms[p]:
515                if h.startswith('HKLF ') and h not in usedHKLFhists:
516                    usedHKLFhists.append(h)
517        rdlist = self.OnImportGeneric(reqrdr,
518                                  self.ImportPhaseReaderlist,
519                                  'phase',usedRanIdList=phaseRIdList)
520        if len(rdlist) == 0: return
521        # for now rdlist is only expected to have one element
522        # but below will allow multiple phases to be imported
523        # if ever the import routines ever implement multiple phase reads.
524        self.CheckNotebook()
525        newPhaseList = []
526        for rd in rdlist:
527            dlg = wx.TextEntryDialog( # allow editing of phase name
528                self, 'Enter the name for the new phase',
529                'Edit phase name', rd.Phase['General']['Name'],
530                style=wx.OK)
531            dlg.CenterOnParent()
532            if dlg.ShowModal() == wx.ID_OK:
533                rd.Phase['General']['Name'] = dlg.GetValue()
534            dlg.Destroy()
535            # make new phase names unique
536            rd.Phase['General']['Name'] = G2obj.MakeUniqueLabel(rd.Phase['General']['Name'],phaseNameList)
537            PhaseName = rd.Phase['General']['Name']
538            newPhaseList.append(PhaseName)
539            print 'Read phase '+str(PhaseName)+' from file '+str(self.lastimport)
540            if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
541                sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
542            else:
543                sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
544            psub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
545            self.PatternTree.SetItemPyData(psub,rd.Phase)
546            self.PatternTree.Expand(self.root) # make sure phases are seen
547            self.PatternTree.Expand(sub) 
548            self.PatternTree.Expand(psub)
549            self.PatternTree.SelectItem(psub) # show the page to complete the initialization (yuk!)
550            wx.Yield() # make sure call of GSASII.OnPatternTreeSelChanged happens before we go on
551
552            if rd.Constraints:
553                sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') # was created in CheckNotebook if needed
554                Constraints = self.PatternTree.GetItemPyData(sub)               
555                # TODO: make sure that NEWVAR names are unique here?
556                for i in rd.Constraints:
557                    if type(i) is dict:
558                        #for j in i: print j,' --> ',i[j]
559                        if '_Explain' not in Constraints: Constraints['_Explain'] = {}
560                        Constraints['_Explain'].update(i)
561                        continue
562                    Constraints['Phase'].append(i)
563        if not newPhaseList: return # somehow, no new phases
564        # get a list of existing histograms
565        PWDRlist = []
566        HKLFlist = []
567        if self.PatternTree.GetCount():
568            item, cookie = self.PatternTree.GetFirstChild(self.root)
569            while item:
570                name = self.PatternTree.GetItemText(item)
571                if name.startswith('PWDR ') and name not in PWDRlist:
572                    PWDRlist.append(name)
573                if name.startswith('HKLF ') and name not in HKLFlist:
574                    HKLFlist.append(name)
575                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
576        TextList = PWDRlist + HKLFlist
577        if not len(TextList): return # no data loaded yet
578        header = 'Select histogram(s) to add to new phase(s):'
579        for phaseName in newPhaseList:
580            header += '\n  '+str(phaseName)
581
582        notOK = True
583        while notOK:
584            result = G2gd.ItemSelector(TextList,self,header,header='Add histogram(s)',multiple=True)
585            if not result: return
586            # check that selected single crystal histograms are not already in use!
587            used = [TextList[i] for i in result if TextList[i] in usedHKLFhists]
588            #for i in result:
589            #    if TextList[i] in usedHKLFhists: used.append(TextList[i])
590            if used:
591                msg = 'The following single crystal histogram(s) are already in use'
592                for i in used:
593                    msg += '\n  '+str(i)
594                msg += '\nAre you sure you want to add them to this phase? '
595                msg += 'Associating a single crystal dataset to >1 histogram is usually an error, '
596                msg += 'so No is suggested here.'
597                if self.ErrorDialog('Likely error',msg,self,wtype=wx.YES_NO) == wx.ID_YES: notOK = False
598            else:
599                notOK = False
600        # connect new phases to histograms
601        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
602        if not sub:
603            raise Exception('ERROR -- why are there no phases here?')
604        wx.BeginBusyCursor()
605        item, cookie = self.PatternTree.GetFirstChild(sub)
606        while item: # loop over (new) phases
607            phaseName = self.PatternTree.GetItemText(item)
608            data = self.PatternTree.GetItemPyData(item)
609            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
610            if phaseName not in newPhaseList: continue
611            generalData = data['General']
612            SGData = generalData['SGData']
613            UseList = data['Histograms']
614            NShkl = len(G2spc.MustrainNames(SGData))
615            NDij = len(G2spc.HStrainNames(SGData))
616            for i in result:
617                histoName = TextList[i]
618                if histoName in HKLFlist:
619                    UseList[histoName] = {
620                        'Histogram':histoName,'Show':False,'Scale':[1.0,True],
621                        'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
622                        'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
623                                                            'Eg':[1.e-10,False],
624                                                            'Es':[1.e-10,False],
625                                                            'Ep':[1.e-10,False]},]
626                        }
627                    #redo UpdateHKLFdata(histoName) here:
628                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
629                    refDict,reflData = self.PatternTree.GetItemPyData(Id)
630                    G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
631                    for iref,ref in enumerate(reflData['RefList']):
632                        H = list(ref[:3])
633                        ref[4] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
634                        iabsnt,ref[3],Uniq,phi = G2spc.GenHKLf(H,SGData)
635                elif histoName in PWDRlist:
636                    UseList[histoName] = {
637                        'Histogram':histoName,'Show':False,
638                        'Scale':[1.0,False],'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{}],
639                        'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
640                                [1.,1.,1.,0.,0.,0.],6*[False,]],
641                        'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
642                                    NShkl*[0.01,],NShkl*[False,]],
643                        'HStrain':[NDij*[0.0,],NDij*[False,]],                         
644                        'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
645                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
646                    refList = self.PatternTree.GetItemPyData(
647                        G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
648                    refList[generalData['Name']] = []
649                else:
650                    raise Exception('Unexpected histogram '+str(histoName))
651        wx.EndBusyCursor()
652        return # success
653       
654    def _Add_ImportMenu_Sfact(self,parent):
655        '''configure the Import Structure Factor menus accord to the readers found in _init_Imports
656        '''
657        submenu = wx.Menu()
658        item = parent.AppendMenu(wx.ID_ANY, 'Structure Factor',
659            submenu, help='Import Structure Factor data')
660        for reader in self.ImportSfactReaderlist:
661            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,               
662                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
663            self.ImportMenuId[item.GetId()] = reader
664            self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
665        item = submenu.Append(wx.ID_ANY,
666            help='Import Structure Factor, use file to try to determine format',
667            kind=wx.ITEM_NORMAL,
668            text='guess format from file')
669        self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
670
671    def OnImportSfact(self,event):
672        '''Called in response to an Import/Structure Factor/... menu item
673        to read single crystal datasets.
674        dict self.ImportMenuId is used to look up the specific
675        reader item associated with the menu item, which will be
676        None for the last menu item, which is the "guess" option
677        where all appropriate formats will be tried.
678        '''
679        # get a list of existing histograms
680        HKLFlist = []
681        if self.PatternTree.GetCount():
682            item, cookie = self.PatternTree.GetFirstChild(self.root)
683            while item:
684                name = self.PatternTree.GetItemText(item)
685                if name.startswith('HKLF ') and name not in HKLFlist:
686                    HKLFlist.append(name)
687                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
688        # look up which format was requested
689        reqrdr = self.ImportMenuId.get(event.GetId())
690        rdlist = self.OnImportGeneric(reqrdr,self.ImportSfactReaderlist,
691            'Structure Factor')
692        if len(rdlist) == 0: return
693        self.CheckNotebook()
694        newHistList = []
695        for rd in rdlist:
696            HistName = rd.objname
697            if len(rdlist) <= 2: 
698                dlg = wx.TextEntryDialog( # allow editing of Structure Factor name
699                    self, 'Enter the name for the new Structure Factor',
700                    'Edit Structure Factor name', HistName,
701                    style=wx.OK)
702                dlg.CenterOnParent()
703                if dlg.ShowModal() == wx.ID_OK:
704                    HistName = dlg.GetValue()
705                dlg.Destroy()
706            HistName = 'HKLF '+HistName
707            # make new histogram names unique
708            HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
709            print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
710            if not rd.RefDict.get('FF'):
711                rd.RefDict['FF'] = [{} for i in range(len(rd.RefDict['RefList']))]
712            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
713            valuesdict = {
714                'wtFactor':1.0,
715                'Dummy':False,
716                'ranId':ran.randint(0,sys.maxint),
717                }
718            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.RefDict])
719            Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
720            self.PatternTree.SetItemPyData(Sub,rd.Parameters)
721            self.PatternTree.SetItemPyData(
722                self.PatternTree.AppendItem(Id,text='HKL Plot Controls'),
723                rd.Controls)
724            self.PatternTree.SetItemPyData(
725                self.PatternTree.AppendItem(Id,text='Reflection List'),[])  #dummy entry for GUI use
726            self.PatternTree.SelectItem(Id)
727            self.PatternTree.Expand(Id)
728            self.Sngl = Id
729            newHistList.append(HistName)
730
731        if not newHistList: return # somehow, no new histograms
732        # make a list of phase names
733        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
734        phaseNameList = usedHistograms.keys() # phase names in use
735        if not phaseNameList: return # no phases yet, nothing to do
736        header = 'Select phase(s) to add the new\nsingle crystal dataset(s) to:'
737        for Name in newHistList:
738            header += '\n  '+str(Name)
739        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
740        if not result: return
741        # connect new phases to histograms
742        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
743        if not sub:
744            raise Exception('ERROR -- why are there no phases here?')
745        wx.BeginBusyCursor()
746        item, cookie = self.PatternTree.GetFirstChild(sub)
747        iph = -1
748        while item: # loop over (new) phases
749            iph += 1
750            phaseName = self.PatternTree.GetItemText(item)
751            data = self.PatternTree.GetItemPyData(item)
752            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
753            if iph not in result: continue
754            generalData = data['General']
755            SGData = generalData['SGData']
756            UseList = data['Histograms']
757            NShkl = len(G2spc.MustrainNames(SGData))
758            NDij = len(G2spc.HStrainNames(SGData))
759            for histoName in newHistList:
760                UseList[histoName] = {
761                    'Histogram':histoName,'Show':False,'Scale':[1.0,True],
762                    'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
763                    'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
764                                                        'Eg':[1.e-10,False],
765                                                        'Es':[1.e-10,False],
766                                                        'Ep':[1.e-10,False]},]
767                    }
768                #redo UpdateHKLFdata(histoName) here:
769                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
770                refDict,reflData = self.PatternTree.GetItemPyData(Id)
771                G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
772                for iref,ref in enumerate(reflData['RefList']):
773                    H = list(ref[:3])
774                    ref[4] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
775                    iabsnt,ref[3],Uniq,phi = G2spc.GenHKLf(H,SGData)
776        wx.EndBusyCursor()
777       
778        return # success
779
780    def _Add_ImportMenu_powder(self,parent):
781        '''configure the Powder Data menus accord to the readers found in _init_Imports
782        '''
783        submenu = wx.Menu()
784        item = parent.AppendMenu(wx.ID_ANY, 'Powder Data',
785            submenu, help='Import Powder data')
786        for reader in self.ImportPowderReaderlist:
787            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
788                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
789            self.ImportMenuId[item.GetId()] = reader
790            self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
791        item = submenu.Append(wx.ID_ANY,
792            help='Import powder data, use file to try to determine format',
793            kind=wx.ITEM_NORMAL,text='guess format from file')
794        self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
795        submenu.AppendSeparator()
796        item = submenu.Append(wx.ID_ANY,
797            help='Create a powder data set entry that will be simulated',
798            kind=wx.ITEM_NORMAL,text='Simulate a dataset')
799        self.Bind(wx.EVT_MENU, self.OnDummyPowder, id=item.GetId())
800           
801    def ReadPowderInstprm(self,instfile):       #fix the write routine for [inst1,inst2] style
802        '''Read a GSAS-II (new) instrument parameter file
803
804        :param str instfile: name of instrument parameter file
805
806        '''
807        if os.path.splitext(instfile)[1].lower() != '.instprm': # invalid file
808            return None           
809        if not os.path.exists(instfile): # no such file
810            return None
811        File = open(instfile,'r')
812        S = File.readline()
813        if not S.startswith('#GSAS-II'): # not a valid file
814            File.close()
815            return None
816        newItems = []
817        newVals = []
818        while S:
819            if S[0] == '#':
820                S = File.readline()
821                continue
822            [item,val] = S[:-1].split(':')
823            newItems.append(item)
824            try:
825                newVals.append(float(val))
826            except ValueError:
827                newVals.append(val)                       
828            S = File.readline()               
829        File.close()
830        # add a second MT dict here. TOF parms? (BHT)
831        return G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}
832       
833    def ReadPowderIparm(self,instfile,bank,databanks,rd):
834        '''Read a GSAS (old) instrument parameter file
835
836        :param str instfile: name of instrument parameter file
837
838        :param int bank: the bank number read in the raw data file
839
840        :param int databanks: the number of banks in the raw data file.
841          If the number of banks in the data and instrument parameter files
842          agree, then the sets of banks are assumed to match up and bank
843          is used to select the instrument parameter file. If not, the user
844          is asked to make a selection.
845
846        :param obj rd: the raw data (histogram) data object. This
847          sets rd.instbank.
848
849        '''
850        if not os.path.exists(instfile): # no such file
851            return {}
852        fp = 0
853        try:
854            fp = open(instfile,'Ur')
855            Iparm = {}
856            for S in fp:
857                Iparm[S[:12]] = S[12:-1]
858        except IOError:
859            print('Error reading file:'+str(instfile))
860        if fp:       
861            fp.close()
862
863        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
864        hType = Iparm['INS   HTYPE '].strip()
865        if ibanks == 1: # there is only one bank here, return it
866            rd.instbank = 1
867            return Iparm
868        if 'PNT' in hType:
869            rd.instbank = bank
870        elif ibanks != databanks:
871            # number of banks in data and prm file not not agree, need a
872            # choice from a human here
873            choices = []
874            for i in range(1,1+ibanks):
875                choices.append('Bank '+str(i))
876            bank = rd.BlockSelector(
877                choices, self,
878                title='Select an instrument parameter bank for '+
879                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
880                '\nOr use Cancel to select from the default parameter sets',
881                header='Block Selector')
882        if bank is None: return {}
883        # pull out requested bank # bank from the data, and change the bank to 1
884        IparmS = {}
885        for key in Iparm:
886            if key[4:6] == "  ":
887                IparmS[key] = Iparm[key]
888            elif int(key[4:6].strip()) == bank:
889                IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
890        rd.instbank = bank
891        return IparmS
892                       
893    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
894        '''Open and read an instrument parameter file for a data file
895        Returns the list of parameters used in the data tree
896
897        :param obj rd: the raw data (histogram) data object.
898
899        :param str prevIparm: not used
900
901        :param str lastIparmfile: Name of last instrument parameter
902          file that was read, or a empty string.
903
904        :param str lastdatafile: Name of last data file that was read.
905
906        :returns: a list of two dicts, the first containing instrument parameters
907          and the second used for future TOF datasets (timemaps?)
908
909        '''
910        def SetPowderInstParms(Iparm, rd):
911            '''extracts values from instrument parameters in rd.instdict
912            or in array Iparm.
913            Create and return the contents of the instrument parameter tree entry.
914            '''
915            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
916            # override inst values with values read from data file
917            if rd.instdict.get('type'):
918                DataType = rd.instdict.get('type')
919            data = [DataType,]
920            instname = Iparm.get('INS  1INAME ')
921            if instname:
922                rd.Sample['InstrName'] = instname.strip()
923            if 'C' in DataType:
924                wave1 = None
925                wave2 = 0.0
926                if rd.instdict.get('wave'):
927                    wl = rd.instdict.get('wave')
928                    wave1 = wl[0]
929                    if len(wl) > 1: wave2 = wl[1]
930                s = Iparm['INS  1 ICONS']
931                if not wave1:
932                    wave1 = G2IO.sfloat(s[:10])
933                    wave2 = G2IO.sfloat(s[10:20])
934                v = (wave1,wave2,
935                     G2IO.sfloat(s[20:30]),G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
936                if not v[1]:
937                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
938                    v = (v[0],v[2],v[4])
939                    codes = [0,0,0,0]
940                else:
941                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
942                    codes = [0,0,0,0,0,0]
943                data.extend(v)
944                v1 = Iparm['INS  1PRCF1 '].split()                                                 
945                v = Iparm['INS  1PRCF11'].split()
946                data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
947                azm = float(Iparm.get('INS  1DETAZM','0.0'))
948                v = Iparm['INS  1PRCF12'].split()
949                if v1[0] == 3:
950                    data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
951                else:
952                    data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
953                codes.extend([0,0,0,0,0,0,0])
954                return [G2IO.makeInstDict(names,data,codes),{}]
955            elif 'T' in DataType:
956                names = ['Type','2-theta','difC','difA','Zero','alpha','beta-0','beta-1',
957                    'beta-q','sig-0','sig-1','sig-q','X','Y','Azimuth']
958                codes = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
959                azm = 0.
960                if 'INS  1DETAZM' in Iparm:
961                    azm = float(Iparm['INS  1DETAZM'])
962                s = Iparm['INS  1BNKPAR'].split()
963                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
964                s = Iparm['INS  1 ICONS'].split()
965                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])    #difC, difA, Zero
966                s = Iparm['INS  1PRCF1 '].split()
967                pfType = int(s[0])
968                s = Iparm['INS  1PRCF11'].split()
969                if abs(pfType) == 1:
970                    data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])])
971                    s = Iparm['INS  1PRCF12'].split()
972                    data.extend([0.0,0.0,G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])
973                elif abs(pfType) in [3,4,5]:
974                    data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])
975                    if abs(pfType) == 4:
976                        data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,azm])
977                    else:
978                        s = Iparm['INS  1PRCF12'].split()
979                        data.extend([0.0,0.0,G2IO.sfloat(s[0]),0.0,0.0,0.0,azm])                       
980                Inst1 = G2IO.makeInstDict(names,data,codes)
981                Inst2 = {}
982                if pfType < 0:
983                    Ipab = 'INS  1PAB'+str(-pfType)
984                    Npab = int(Iparm[Ipab+'  '].strip())
985                    Inst2['Pdabc'] = []
986                    for i in range(Npab):
987                        k = Ipab+str(i+1).rjust(2)
988                        s = Iparm[k].split()
989                        Inst2['Pdabc'].append([float(t) for t in s])
990                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
991                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
992                if 'INS  1I ITYP' in Iparm:
993                    s = Iparm['INS  1I ITYP'].split()
994                    Ityp = int(s[0])
995                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
996                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
997                    if Ityp in [1,2,4]:
998                        Inst2['Itype'] = Itypes[Ityp-1]
999                        Inst2['Tminmax'] = Tminmax
1000                        Icoeff = []
1001                        Iesd = []
1002                        Icovar = []                   
1003                        for i in range(3):
1004                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
1005                            Icoeff += [float(S) for S in s]
1006                            s = Iparm['INS  1IECOF'+str(i+1)].split()
1007                            Iesd += [float(S) for S in s]
1008                        for i in range(8):
1009                            s = Iparm['INS  1IECOR'+str(i+1)].split()
1010                            Icovar += [float(S) for S in s]
1011                        Inst2['Icoeff'] = Icoeff
1012                        Inst2['Iesd'] = Iesd
1013                        Inst2['Icovar'] = Icovar
1014                return [Inst1,Inst2]
1015
1016        # stuff we might need from the reader
1017        filename = rd.powderentry[0]
1018        bank = rd.powderentry[2]
1019        numbanks = rd.numbanks
1020        # is there an instrument parameter file defined for the current data set?
1021        if rd.instparm or (lastdatafile == filename and lastIparmfile):
1022            if rd.instparm:
1023                instfile = os.path.join(os.path.split(filename)[0],
1024                                    rd.instparm)
1025            else:
1026                # for multiple reads of one data file, reuse the inst parm file
1027                instfile = lastIparmfile
1028            if os.path.exists(instfile):
1029                #print 'debug: try read',instfile
1030                instParmList = self.ReadPowderInstprm(instfile)
1031                if instParmList is not None:
1032                    rd.instfile = instfile
1033                    rd.instmsg = 'GSAS-II file '+instfile
1034                    return instParmList
1035                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1036                if Iparm:
1037                    #print 'debug: success'
1038                    rd.instfile = instfile
1039                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1040                    return SetPowderInstParms(Iparm,rd)
1041            else:
1042                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1043                    +str(instfile)+' requested by file '+ filename)
1044        # is there an instrument parameter file matching the current file
1045        # with extension .inst or .prm? If so read it
1046        basename = os.path.splitext(filename)[0]
1047        for ext in '.instprm','.prm','.inst','.ins':
1048            instfile = basename + ext
1049            instParmList = self.ReadPowderInstprm(instfile)
1050            if instParmList is not None:
1051                rd.instfile = instfile
1052                rd.instmsg = 'GSAS-II file '+instfile
1053                return instParmList
1054            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1055            if Iparm:
1056                #print 'debug: success'
1057                rd.instfile = instfile
1058                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1059                return SetPowderInstParms(Iparm,rd)
1060            else:
1061                #print 'debug: open/read failed',instfile
1062                pass # fail silently
1063
1064        # did we read the data file from a zip? If so, look there for a
1065        # instrument parameter file
1066        if self.zipfile:
1067            for ext in '.instprm','.prm','.inst','.ins':
1068                instfile = G2IO.ExtractFileFromZip(
1069                    self.zipfile,
1070                    selection=os.path.split(basename + ext)[1],
1071                    parent=self)
1072                if instfile is not None and instfile != self.zipfile:
1073                    print 'debug:',instfile,'created from ',self.zipfile
1074                    instParmList = self.ReadPowderInstprm(instfile)
1075                    if instParmList is not None:
1076                        rd.instfile = instfile
1077                        rd.instmsg = 'GSAS-II file '+instfile
1078                        return instParmList
1079                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1080                    if Iparm:
1081                        rd.instfile = instfile
1082                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1083                        return SetPowderInstParms(Iparm,rd)
1084                    else:
1085                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
1086                        pass # fail silently
1087
1088        while True: # loop until we get a file that works or we get a cancel
1089            instfile = ''
1090            dlg = wx.FileDialog(
1091                self,
1092                'Choose inst. param file for "'
1093                +rd.idstring
1094                +'" (or Cancel for default)',
1095                '.', '',
1096                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1097                'GSAS-II iparm file (*.instprm)|*.instprm|'
1098                'All files (*.*)|*.*', 
1099                wx.OPEN|wx.CHANGE_DIR)
1100            if os.path.exists(lastIparmfile):
1101                dlg.SetFilename(lastIparmfile)
1102            if dlg.ShowModal() == wx.ID_OK:
1103                instfile = dlg.GetPath()
1104            dlg.Destroy()
1105            if not instfile: break
1106            instParmList = self.ReadPowderInstprm(instfile)
1107            if instParmList is not None:
1108                rd.instfile = instfile
1109                rd.instmsg = 'GSAS-II file '+instfile
1110                return instParmList
1111            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1112            if Iparm:
1113                #print 'debug: success with',instfile
1114                rd.instfile = instfile
1115                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1116                return SetPowderInstParms(Iparm,rd)
1117            else:
1118                self.ErrorDialog('Read Error',
1119                                 'Error opening/reading file '+str(instfile))
1120       
1121        # still no success: offer user choice of defaults
1122        while True: # loop until we get a choice
1123            choices = []
1124            head = 'Select from default instrument parameters for '+rd.idstring
1125
1126            for l in rd.defaultIparm_lbl:
1127                choices.append('Defaults for '+l)
1128            res = rd.BlockSelector(
1129                choices,
1130                ParentFrame=self,
1131                title=head,
1132                header='Select default inst parms',
1133                useCancel=False)
1134            if res is None: continue
1135            rd.instfile = ''
1136            rd.instmsg = 'default: '+rd.defaultIparm_lbl[res]
1137            return SetPowderInstParms(rd.defaultIparms[res],rd)
1138
1139    def OnImportPowder(self,event):
1140        '''Called in response to an Import/Powder Data/... menu item
1141        to read a powder diffraction data set.
1142        dict self.ImportMenuId is used to look up the specific
1143        reader item associated with the menu item, which will be
1144        None for the last menu item, which is the "guess" option
1145        where all appropriate formats will be tried.
1146
1147        Also reads an instrument parameter file for each dataset.
1148        '''
1149        # get a list of existing histograms
1150        PWDRlist = []
1151        if self.PatternTree.GetCount():
1152            item, cookie = self.PatternTree.GetFirstChild(self.root)
1153            while item:
1154                name = self.PatternTree.GetItemText(item)
1155                if name.startswith('PWDR ') and name not in PWDRlist:
1156                    PWDRlist.append(name)
1157                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1158        # look up which format was requested
1159        reqrdr = self.ImportMenuId.get(event.GetId()) 
1160        rdlist = self.OnImportGeneric(
1161            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1162        if len(rdlist) == 0: return
1163        self.CheckNotebook()
1164        Iparm = None
1165        lastIparmfile = ''
1166        lastdatafile = ''
1167        newHistList = []
1168        for rd in rdlist:
1169            # get instrument parameters for each dataset
1170            Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1171            if rd.repeat_instparm: 
1172                lastIparmfile = rd.instfile
1173            lastdatafile = rd.powderentry[0]
1174            HistName = rd.idstring
1175            HistName = 'PWDR '+HistName
1176            # make new histogram names unique
1177            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1178            print 'Read powder data '+str(HistName)+ \
1179                ' from file '+str(self.lastimport) + \
1180                ' with parameters from '+str(rd.instmsg)
1181            # data are read, now store them in the tree
1182            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1183            if 'T' in Iparm1['Type'][0]:
1184                if not rd.clockWd and rd.GSAS:
1185                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1186                cw = np.diff(rd.powderdata[0])
1187                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1188                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1189                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1190                if 'Itype' in Iparm2:
1191                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1192                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1193                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1194                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1195                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1196                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1197                    var += WYI*rd.powderdata[1]**2
1198                    var /= YI**2
1199                    rd.powderdata[2] = 1./var
1200                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1201                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1202                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1203            Tmin = min(rd.powderdata[0])
1204            Tmax = max(rd.powderdata[0])
1205            valuesdict = {
1206                'wtFactor':1.0,
1207                'Dummy':False,
1208                'ranId':ran.randint(0,sys.maxint),
1209                }
1210            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1211            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1212            self.PatternTree.SetItemPyData(
1213                self.PatternTree.AppendItem(Id,text='Comments'),
1214                rd.comments)
1215            self.PatternTree.SetItemPyData(
1216                self.PatternTree.AppendItem(Id,text='Limits'),
1217                [(Tmin,Tmax),[Tmin,Tmax]])
1218            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1219            self.PatternTree.SetItemPyData(
1220                self.PatternTree.AppendItem(Id,text='Background'),
1221                [['chebyschev',True,3,1.0,0.0,0.0],
1222                 {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1223            self.PatternTree.SetItemPyData(
1224                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1225                [Iparm1,Iparm2])
1226            self.PatternTree.SetItemPyData(
1227                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1228                rd.Sample)
1229            self.PatternTree.SetItemPyData(
1230                self.PatternTree.AppendItem(Id,text='Peak List')
1231                ,[])
1232            self.PatternTree.SetItemPyData(
1233                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1234                [])
1235            self.PatternTree.SetItemPyData(
1236                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1237                [])
1238            self.PatternTree.SetItemPyData(
1239                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1240                {})
1241            self.PatternTree.Expand(Id)
1242            self.PatternTree.SelectItem(Id)
1243            newHistList.append(HistName)
1244           
1245        if not newHistList: return # somehow, no new histograms
1246        # make a list of phase names
1247        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1248        phaseNameList = usedHistograms.keys() # phase names in use
1249        if not phaseNameList: return # no phases yet, nothing to do
1250        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1251        for Name in newHistList:
1252            header += '\n  '+str(Name)
1253
1254        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1255        if not result: return
1256        # connect new phases to histograms
1257        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1258        if not sub:
1259            raise Exception('ERROR -- why are there no phases here?')
1260        item, cookie = self.PatternTree.GetFirstChild(sub)
1261        iph = -1
1262        while item: # loop over (new) phases
1263            iph += 1
1264            phaseName = self.PatternTree.GetItemText(item)
1265            data = self.PatternTree.GetItemPyData(item)
1266            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1267            if iph not in result: continue
1268            generalData = data['General']
1269            SGData = generalData['SGData']
1270            UseList = data['Histograms']
1271            NShkl = len(G2spc.MustrainNames(SGData))
1272            NDij = len(G2spc.HStrainNames(SGData))
1273            for histoName in newHistList:
1274                UseList[histoName] = {
1275                    'Histogram':histoName,'Show':False,
1276                    'Scale':[1.0,False],'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{}],
1277                    'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
1278                            [1.,1.,1.,0.,0.,0.],6*[False,]],
1279                    'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
1280                                NShkl*[0.01,],NShkl*[False,]],
1281                    'HStrain':[NDij*[0.0,],NDij*[False,]],                         
1282                    'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
1283                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1284                refList = self.PatternTree.GetItemPyData(
1285                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1286                refList[generalData['Name']] = []
1287        return # success
1288
1289    def OnDummyPowder(self,event):
1290        '''Called in response to Import/Powder Data/Simulate menu item
1291        to create a Dummy powder diffraction data set.
1292
1293        Reads an instrument parameter file and then gets input from the user
1294        '''
1295        # get a list of existing histograms
1296        PWDRlist = []
1297        if self.PatternTree.GetCount():
1298            item, cookie = self.PatternTree.GetFirstChild(self.root)
1299            while item:
1300                name = self.PatternTree.GetItemText(item)
1301                if name.startswith('PWDR ') and name not in PWDRlist:
1302                    PWDRlist.append(name)
1303                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1304        # Initialize a base class reader
1305        rd = G2IO.ImportPowderData(
1306            extensionlist=tuple(),
1307            strictExtension=False,
1308            formatName = 'Simulate dataset',
1309            longFormatName = 'Compute a simulated pattern')
1310        rd.powderentry[0] = '' # no filename
1311        # #self.powderentry[1] = pos # bank offset (N/A here)
1312        rd.powderentry[2] = 1 # only one bank
1313        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1314        self.CheckNotebook()
1315        Iparm = None
1316        lastIparmfile = ''
1317        lastdatafile = ''
1318        self.zipfile = None
1319        # get instrument parameters for it
1320        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1321        if 'T' in Iparm1['Type'][0]:
1322            print('TOF simulation not supported yet')
1323            return False
1324        else:
1325            # need to get name, 2theta start, end, step
1326            rd.idstring = ' CW'
1327            if 'X' in Iparm1['Type'][0]:
1328                rd.idstring = 'CW x-ray simulation'
1329            else:
1330                rd.idstring = 'CW neutron simulation'
1331            # base initial range on wavelength
1332            wave = Iparm1.get('Lam')
1333            if wave:
1334                wave = wave[0]
1335            else:
1336                wave = Iparm1.get('Lam1')
1337                if wave:
1338                    wave = wave[0]
1339        N = 0
1340        while (N < 3): # insist on a dataset with a few points
1341            names = ('dataset name', 'start angle', 'end angle', 'step size')
1342            if not wave or wave < 1.0:
1343                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1344            else:
1345                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1346            dlg = G2gd.ScrolledMultiEditor(
1347                self,[inp] * len(inp),range(len(inp)),names,
1348                header='Enter simulation name and range',
1349                minvals=(None,0.001,0.001,0.0001),
1350                maxvals=(None,180.,180.,.1),
1351                sizevals=((225,-1),)
1352                )
1353            dlg.CenterOnParent()
1354            if dlg.ShowModal() == wx.ID_OK:
1355                if inp[1] > inp[2]:
1356                    end,start,step = inp[1:]
1357                else:               
1358                    start,end,step = inp[1:]
1359                step = abs(step)
1360            else:
1361                return False
1362            N = int((end-start)/step)+1
1363            x = np.linspace(start,end,N,True)
1364            N = len(x)
1365        rd.powderdata = [
1366            np.array(x), # x-axis values
1367            np.zeros_like(x), # powder pattern intensities
1368            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1369            np.zeros_like(x), # calc. intensities (zero)
1370            np.zeros_like(x), # calc. background (zero)
1371            np.zeros_like(x), # obs-calc profiles
1372            ]
1373        Tmin = rd.powderdata[0][0]
1374        Tmax = rd.powderdata[0][-1]
1375        # data are read, now store them in the tree
1376        HistName = inp[0]
1377        HistName = 'PWDR '+HistName
1378        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1379        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1380        valuesdict = {
1381            'wtFactor':1.0,
1382            'Dummy':True,
1383            'ranId':ran.randint(0,sys.maxint),
1384            }
1385        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1386        self.PatternTree.SetItemPyData(
1387            self.PatternTree.AppendItem(Id,text='Comments'),
1388            rd.comments)
1389        self.PatternTree.SetItemPyData(
1390            self.PatternTree.AppendItem(Id,text='Limits'),
1391            [(Tmin,Tmax),[Tmin,Tmax]])
1392        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1393        self.PatternTree.SetItemPyData(
1394            self.PatternTree.AppendItem(Id,text='Background'),
1395            [['chebyschev',True,3,1.0,0.0,0.0],
1396             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1397        self.PatternTree.SetItemPyData(
1398            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1399            [Iparm1,Iparm2])
1400        self.PatternTree.SetItemPyData(
1401            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1402            rd.Sample)
1403        self.PatternTree.SetItemPyData(
1404            self.PatternTree.AppendItem(Id,text='Peak List')
1405            ,[])
1406        self.PatternTree.SetItemPyData(
1407            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1408            [])
1409        self.PatternTree.SetItemPyData(
1410            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1411            [])
1412        self.PatternTree.SetItemPyData(
1413            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1414            {})
1415        self.PatternTree.Expand(Id)
1416        self.PatternTree.SelectItem(Id)
1417        print('Added simulation powder data '+str(HistName)+ 
1418              ' with parameters from '+str(rd.instmsg))
1419
1420        # make a list of phase names
1421        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1422        phaseNameList = usedHistograms.keys() # phase names in use
1423        if not phaseNameList: return # no phases yet, nothing to do
1424        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1425        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1426        if not result: return
1427        # connect new phases to histograms
1428        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1429        if not sub:
1430            raise Exception('ERROR -- why are there no phases here?')
1431        item, cookie = self.PatternTree.GetFirstChild(sub)
1432        iph = -1
1433        while item: # loop over (new) phases
1434            iph += 1
1435            phaseName = self.PatternTree.GetItemText(item)
1436            data = self.PatternTree.GetItemPyData(item)
1437            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1438            if iph not in result: continue
1439            generalData = data['General']
1440            SGData = generalData['SGData']
1441            UseList = data['Histograms']
1442            NShkl = len(G2spc.MustrainNames(SGData))
1443            NDij = len(G2spc.HStrainNames(SGData))
1444            UseList[HistName] = {
1445                'Histogram':HistName,'Show':False,
1446                'Scale':[1.0,False],'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{}],
1447                'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
1448                        [1.,1.,1.,0.,0.,0.],6*[False,]],
1449                'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
1450                            NShkl*[0.01,],NShkl*[False,]],
1451                'HStrain':[NDij*[0.0,],NDij*[False,]],                         
1452                'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
1453            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1454            refList = self.PatternTree.GetItemPyData(
1455                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1456            refList[generalData['Name']] = []
1457        return # success
1458
1459    def _Add_ImportMenu_smallangle(self,parent):
1460        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1461        '''
1462        submenu = wx.Menu()
1463        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1464            submenu, help='Import small angle data')
1465        for reader in self.ImportSmallAngleReaderlist:
1466            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1467                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1468            self.ImportMenuId[item.GetId()] = reader
1469            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1470        # item = submenu.Append(wx.ID_ANY,
1471        #     help='Import small angle data, use file to try to determine format',
1472        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1473        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1474
1475    def OnImportSmallAngle(self,event):
1476        '''Called in response to an Import/Small Angle Data/... menu item
1477        to read a small angle diffraction data set.
1478        dict self.ImportMenuId is used to look up the specific
1479        reader item associated with the menu item, which will be
1480        None for the last menu item, which is the "guess" option
1481        where all appropriate formats will be tried.
1482
1483        '''
1484       
1485        def GetSASDIparm(reader):
1486            parm = reader.instdict
1487            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1488                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1489            return Iparm,{}
1490           
1491        # get a list of existing histograms
1492        SASDlist = []
1493        if self.PatternTree.GetCount():
1494            item, cookie = self.PatternTree.GetFirstChild(self.root)
1495            while item:
1496                name = self.PatternTree.GetItemText(item)
1497                if name.startswith('SASD ') and name not in SASDlist:
1498                    SASDlist.append(name)
1499                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1500        # look up which format was requested
1501        reqrdr = self.ImportMenuId.get(event.GetId()) 
1502        rdlist = self.OnImportGeneric(
1503            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1504        if len(rdlist) == 0: return
1505        self.CheckNotebook()
1506        Iparm = None
1507        lastdatafile = ''
1508        newHistList = []
1509        for rd in rdlist:
1510            lastdatafile = rd.smallangleentry[0]
1511            HistName = rd.idstring
1512            HistName = 'SASD '+HistName
1513            # make new histogram names unique
1514            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1515            print 'Read small angle data '+str(HistName)+ \
1516                ' from file '+str(self.lastimport)
1517            # data are read, now store them in the tree
1518            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1519            Iparm1,Iparm2 = GetSASDIparm(rd)
1520#            if 'T' in Iparm1['Type'][0]:
1521#                if not rd.clockWd and rd.GSAS:
1522#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1523#                cw = np.diff(rd.powderdata[0])
1524#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1525#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1526#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1527#                if 'Itype' in Iparm2:
1528#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1529#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1530#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1531#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1532#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1533#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1534#                    var += WYI*rd.powderdata[1]**2
1535#                    var /= YI**2
1536#                    rd.powderdata[2] = 1./var
1537#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1538#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1539#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1540            Tmin = min(rd.smallangledata[0])
1541            Tmax = max(rd.smallangledata[0])
1542            valuesdict = {
1543                'wtFactor':1.0,
1544                'Dummy':False,
1545                'ranId':ran.randint(0,sys.maxint),
1546                }
1547            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1548            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1549            self.PatternTree.SetItemPyData(
1550                self.PatternTree.AppendItem(Id,text='Comments'),
1551                rd.comments)
1552            self.PatternTree.SetItemPyData(
1553                self.PatternTree.AppendItem(Id,text='Limits'),
1554                [(Tmin,Tmax),[Tmin,Tmax]])
1555            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1556            self.PatternTree.SetItemPyData(
1557                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1558                [Iparm1,Iparm2])
1559            self.PatternTree.SetItemPyData(
1560                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1561            self.PatternTree.SetItemPyData(
1562                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1563                rd.Sample)
1564            self.PatternTree.SetItemPyData(
1565                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1566            self.PatternTree.Expand(Id)
1567            self.PatternTree.SelectItem(Id)
1568            newHistList.append(HistName)
1569           
1570        if not newHistList: return # somehow, no new histograms
1571        return # success
1572
1573    def _init_Exports(self,menu):
1574        '''Find exporter routines and add them into menus
1575        '''
1576        # set up the top-level menus
1577        projectmenu = wx.Menu()
1578        item = menu.AppendMenu(
1579            wx.ID_ANY, 'Entire project as',
1580            projectmenu, help='Export entire project')
1581
1582        phasemenu = wx.Menu()
1583        item = menu.AppendMenu(
1584            wx.ID_ANY, 'Phase as',
1585            phasemenu, help='Export phase or sometimes phases')
1586
1587        powdermenu = wx.Menu()
1588        item = menu.AppendMenu(
1589            wx.ID_ANY, 'Powder data as',
1590            powdermenu, help='Export powder diffraction histogram(s)')
1591
1592        singlemenu = wx.Menu()
1593        item = menu.AppendMenu(
1594            wx.ID_ANY, 'Single crystal data as',
1595            singlemenu, help='Export single crystal histogram(s)')
1596
1597        imagemenu = wx.Menu()
1598        item = menu.AppendMenu(
1599            wx.ID_ANY, 'Image data as',
1600            imagemenu, help='Export powder image(s) data')
1601
1602        mapmenu = wx.Menu()
1603        item = menu.AppendMenu(
1604            wx.ID_ANY, 'Maps as',
1605            mapmenu, help='Export density map(s)')
1606
1607        # pdfmenu = wx.Menu()
1608        # item = menu.AppendMenu(
1609        #     wx.ID_ANY, 'PDFs as',
1610        #     pdfmenu, help='Export pair distribution function(s)')
1611
1612        # find all the exporter files
1613        pathlist = sys.path
1614        filelist = []
1615        for path in pathlist:
1616            for filename in glob.iglob(os.path.join(
1617                path,
1618                "G2export*.py")):
1619                filelist.append(filename)   
1620        filelist = sorted(list(set(filelist))) # remove duplicates
1621        exporterlist = []
1622        # go through the routines and import them, saving objects that
1623        # have export routines (method Exporter)
1624        for filename in filelist:
1625            path,rootname = os.path.split(filename)
1626            pkg = os.path.splitext(rootname)[0]
1627            try:
1628                fp = None
1629                fp, fppath,desc = imp.find_module(pkg,[path,])
1630                pkg = imp.load_module(pkg,fp,fppath,desc)
1631                for clss in inspect.getmembers(pkg): # find classes defined in package
1632                    if clss[0].startswith('_'): continue
1633                    if inspect.isclass(clss[1]):
1634                        # check if we have the required methods
1635                        for m in 'Exporter','loadParmDict':
1636                            if not hasattr(clss[1],m): break
1637                            if not callable(getattr(clss[1],m)): break
1638                        else:
1639                            exporter = clss[1](self) # create an export instance
1640                            exporterlist.append(exporter)
1641            except AttributeError:
1642                print 'Import_'+errprefix+': Attribute Error'+str(filename)
1643                pass
1644            except ImportError:
1645                print 'Import_'+errprefix+': Error importing file'+str(filename)
1646                pass
1647            if fp: fp.close()
1648        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
1649        for obj in exporterlist:
1650            #print 'exporter',obj
1651            for typ in obj.exporttype:
1652                if typ == "project":
1653                    submenu = projectmenu
1654                elif typ == "phase":
1655                    submenu = phasemenu
1656                elif typ == "powder":
1657                    submenu = powdermenu
1658                elif typ == "single":
1659                    submenu = singlemenu
1660                elif typ == "image":
1661                    submenu = imagemenu
1662                elif typ == "map":
1663                    submenu = mapmenu
1664                # elif typ == "pdf":
1665                #     submenu = pdfmenu
1666                else:
1667                    print("Error, unknown type in "+str(obj))
1668                    break
1669                item = submenu.Append(
1670                    wx.ID_ANY,
1671                    help=obj.longFormatName,
1672                    kind=wx.ITEM_NORMAL,
1673                    text=obj.formatName)
1674                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
1675                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
1676        #code to debug an Exporter. much is hard-coded below, but code is reloaded before
1677        # each use allowing faster development
1678        # def DebugExport(event):
1679        #     print 'start reload'
1680        #     reload(G2IO)
1681        #     import G2export_ASCIImap as dev
1682        #     reload(dev)
1683        #     dev.ExportMapASCII(self).Exporter(event)
1684        # item = menu.Append(
1685        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1686        #     help="debug exporter",text="test Export")
1687        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
1688        # #self.ExportLookup[item.GetId()] = 'image'
1689        # self.ExportLookup[item.GetId()] = 'map'
1690
1691    def _Add_ExportMenuItems(self,parent):
1692        item = parent.Append(
1693            help='Select PWDR item to enable',id=wx.ID_ANY,
1694            kind=wx.ITEM_NORMAL,
1695            text='Export Powder Patterns...')
1696        self.ExportPattern.append(item)
1697        item.Enable(False)
1698        self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1699
1700        item = parent.Append(
1701            help='',id=wx.ID_ANY,
1702            kind=wx.ITEM_NORMAL,
1703            text='Export All Peak Lists...')
1704        self.ExportPeakList.append(item)
1705        item.Enable(True)
1706        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1707
1708        item = parent.Append(
1709            help='',id=wx.ID_ANY,
1710            kind=wx.ITEM_NORMAL,
1711            text='Export HKLs...')
1712        self.ExportHKL.append(item)
1713        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1714
1715        item = parent.Append(
1716            help='Select PDF item to enable',
1717            id=wx.ID_ANY,
1718            kind=wx.ITEM_NORMAL,
1719            text='Export PDF...')
1720        self.ExportPDF.append(item)
1721        item.Enable(False)
1722        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1723
1724    def FillMainMenu(self,menubar):
1725        '''Define contents of the main GSAS-II menu for the (main) data tree window
1726        in the mac, used also for the data item windows as well.
1727        '''
1728        File = wx.Menu(title='')
1729        menubar.Append(menu=File, title='&File')
1730        self._Add_FileMenuItems(File)
1731        Data = wx.Menu(title='')
1732        menubar.Append(menu=Data, title='Data')
1733        self._Add_DataMenuItems(Data)
1734        Calculate = wx.Menu(title='')       
1735        menubar.Append(menu=Calculate, title='&Calculate')
1736        self._Add_CalculateMenuItems(Calculate)
1737        Import = wx.Menu(title='')       
1738        menubar.Append(menu=Import, title='Import')
1739        self._Add_ImportMenu_Phase(Import)
1740        self._Add_ImportMenu_powder(Import)
1741        self._Add_ImportMenu_Sfact(Import)
1742        self._Add_ImportMenu_smallangle(Import)
1743        #======================================================================
1744        # Code to help develop/debug an importer, much is hard-coded below
1745        # but module is reloaded before each use, allowing faster testing
1746        # def DebugImport(event):
1747        #     print 'start reload'
1748        #     import G2phase_ISO as dev
1749        #     reload(dev)
1750        #     rd = dev.ISODISTORTPhaseReader()
1751        #     self.ImportMenuId[event.GetId()] = rd
1752        #     self.OnImportPhase(event)
1753            # or ----------------------------------------------------------------------
1754            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
1755            # special debug code
1756            # or ----------------------------------------------------------------------
1757            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
1758            # fp = open(filename,'Ur')
1759            # if not rd.ContentsValidator(fp):
1760            #     print 'not validated'
1761            #     # make a list of used phase ranId's
1762            # phaseRIdList = []
1763            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1764            # if sub:
1765            #     item, cookie = self.PatternTree.GetFirstChild(sub)
1766            #     while item:
1767            #         phaseName = self.PatternTree.GetItemText(item)
1768            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
1769            #         if ranId: phaseRIdList.append(ranId)
1770            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1771            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
1772            #     print 'read OK'
1773        # item = Import.Append(
1774        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1775        #     help="debug importer",text="test importer")
1776        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
1777        #======================================================================
1778        self.ExportMenu = wx.Menu(title='')
1779        menubar.Append(menu=self.ExportMenu, title='Export')
1780        self._init_Exports(self.ExportMenu)
1781        self._Add_ExportMenuItems(self.ExportMenu)
1782        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1783            morehelpitems=[('&Tutorials','Tutorials')])
1784        menubar.Append(menu=HelpMenu,title='&Help')
1785
1786    def _init_ctrls(self, parent):
1787        wx.Frame.__init__(self, name='GSASII', parent=parent,
1788            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1789        clientSize = wx.ClientDisplayRect()
1790        Size = self.GetSize()
1791        xPos = clientSize[2]-Size[0]
1792        self.SetPosition(wx.Point(xPos,clientSize[1]))
1793        self._init_Imports()
1794        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1795        self.MakePDF = []
1796        self.Refine = []
1797        self.SeqRefine = []
1798        self.ExportPattern = []
1799        self.ExportPeakList = []
1800        self.ExportHKL = []
1801        self.ExportPDF = []
1802        self.ExportPhase = []
1803        self.ExportCIF = []
1804        #
1805        self.GSASIIMenu = wx.MenuBar()
1806        self.FillMainMenu(self.GSASIIMenu)
1807        self.SetMenuBar(self.GSASIIMenu)
1808        self.Bind(wx.EVT_SIZE, self.OnSize)
1809        self.CreateStatusBar()
1810        self.mainPanel = wx.Panel(self,-1)
1811       
1812        wxID_PATTERNTREE = wx.NewId()
1813        self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE,
1814            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1815        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,
1816            self.OnPatternTreeSelChanged, id=wxID_PATTERNTREE)
1817        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1818            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1819        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1820            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1821        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1822            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1823        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1824            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1825        self.root = self.PatternTree.AddRoot('Loaded Data: ')
1826       
1827        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1828            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1829        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1830        plotFrame.Show()
1831       
1832        self.dataDisplay = None
1833       
1834    def __init__(self, parent):
1835        self.ExportLookup = {}
1836        self._init_ctrls(parent)
1837        self.Image = wx.Image(
1838            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
1839            wx.BITMAP_TYPE_ICO)
1840        if "wxMSW" in wx.PlatformInfo:
1841            img = self.Image.Scale(16, 16).ConvertToBitmap()
1842        elif "wxGTK" in wx.PlatformInfo:
1843            img = self.Image.Scale(22, 22).ConvertToBitmap()
1844        else:
1845            img = self.Image.ConvertToBitmap()
1846        self.SetIcon(wx.IconFromBitmap(img))
1847        self.Bind(wx.EVT_CLOSE, self.ExitMain)
1848        # various defaults
1849        self.oldFocus = None
1850        self.GSASprojectfile = ''
1851        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
1852        self.undofile = ''
1853        self.TreeItemDelete = False
1854        self.Offset = [0.0,0.0]
1855        self.delOffset = .02
1856        self.refOffset = -100.0
1857        self.refDelt = .01
1858        self.Weight = False
1859        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
1860        self.IfPlot = False
1861        self.PatternId = 0
1862        self.PickId = 0
1863        self.PeakTable = []
1864        self.LimitsTable = []
1865        self.HKL = []
1866        self.Lines = []
1867        self.itemPicked = None
1868        self.dataFrame = None
1869        self.Interpolate = 'nearest'
1870        self.ContourColor = 'Paired'
1871        self.VcovColor = 'RdYlGn'
1872        self.RamaColor = 'Blues'
1873        self.Projection = 'equal area'
1874        self.logPlot = False
1875        self.qPlot = False
1876        self.sqPlot = False
1877        self.ErrorBars = False
1878        self.Contour = False
1879        self.Legend = False
1880        self.SinglePlot = False
1881        self.SubBack = False
1882        self.plotView = 0
1883        self.Image = 0
1884        self.oldImagefile = ''
1885        self.ImageZ = []
1886        self.Integrate = 0
1887        self.imageDefault = {}
1888        self.Sngl = 0
1889        self.ifGetRing = False
1890        self.MaskKey = ''           #trigger for making image masks
1891        self.StrainKey = ''         #ditto for new strain d-zeros
1892        arg = sys.argv
1893        if len(arg) > 1:
1894            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1895            self.dirname = os.path.dirname(arg[1])
1896            if self.dirname: os.chdir(self.dirname)
1897            try:
1898                G2IO.ProjFileOpen(self)
1899                self.PatternTree.Expand(self.root)
1900                for item in self.Refine: item.Enable(True)
1901                for item in self.SeqRefine: item.Enable(True)
1902            except:
1903                print 'Error opening file',arg[1]
1904
1905    def OnSize(self,event):
1906        'Called when the main window is resized. Not sure why'
1907        w,h = self.GetClientSizeTuple()
1908        self.mainPanel.SetSize(wx.Size(w,h))
1909        self.PatternTree.SetSize(wx.Size(w,h))
1910                       
1911    def OnPatternTreeSelChanged(self, event):
1912        '''Called when a data tree item is selected'''
1913        if self.TreeItemDelete:
1914            self.TreeItemDelete = False
1915        else:
1916            pltNum = self.G2plotNB.nb.GetSelection()
1917            if pltNum >= 0:                         #to avoid the startup with no plot!
1918                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1919                pltPlot = pltPage.figure
1920            item = event.GetItem()
1921            G2gd.MovePatternTreeToGrid(self,item)
1922            if self.oldFocus:
1923                self.oldFocus.SetFocus()
1924       
1925    def OnPatternTreeItemCollapsed(self, event):
1926        'Called when a tree item is collapsed'
1927        event.Skip()
1928
1929    def OnPatternTreeItemExpanded(self, event):
1930        'Called when a tree item is expanded'
1931        event.Skip()
1932       
1933    def OnPatternTreeItemDelete(self, event):
1934        'Called when a tree item is deleted -- not sure what this does'
1935        self.TreeItemDelete = True
1936
1937    def OnPatternTreeItemActivated(self, event):
1938        'Called when a tree item is activated'
1939        event.Skip()
1940       
1941    def OnPatternTreeKeyDown(self,event):
1942        'Not sure what this does'
1943        key = event.GetKeyCode()
1944        item = self.PickId
1945        if type(item) is int: return # is this the toplevel in tree?
1946        if key == wx.WXK_UP:
1947            self.oldFocus = wx.Window.FindFocus()
1948            self.PatternTree.GetPrevSibling(item)
1949        elif key == wx.WXK_DOWN:
1950            self.oldFocus = wx.Window.FindFocus()
1951            self.PatternTree.GetNextSibling(item)
1952               
1953    def OnReadPowderPeaks(self,event):
1954        'Bound to menu Data/Read Powder Peaks -- still needed?'
1955        Cuka = 1.54052
1956        self.CheckNotebook()
1957        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
1958            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
1959        try:
1960            if dlg.ShowModal() == wx.ID_OK:
1961                self.HKL = []
1962                self.powderfile = dlg.GetPath()
1963                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
1964                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
1965                data = ['PKS',Cuka,0.0]
1966                names = ['Type','Lam','Zero'] 
1967                codes = [0,0,0]
1968                inst = [G2IO.makeInstDict(names,data,codes),{}]
1969                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1970                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
1971                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),peaks)
1972                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1973                self.PatternTree.Expand(Id)
1974                self.PatternTree.SelectItem(Id)
1975                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1976        finally:
1977            dlg.Destroy()
1978                       
1979    def OnImageRead(self,event):
1980        'Called to read in an image in any known format'
1981        self.CheckNotebook()
1982        dlg = wx.FileDialog(
1983            self, 'Choose image files', '.', '',
1984            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.png)|'
1985            '*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.png;*.zip|'
1986            'European detector file (*.edf)|*.edf|'
1987            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
1988            'MAR file (*.mar*)|*.mar*|'
1989            'GE Image (*.avg;*.sum)|*.avg;*.sum|'
1990            'ADSC Image (*.img)|*.img|'
1991            'GSAS-II Image (*.G2img)|*.G2img|'
1992            'Portable Network Graphics image (*.png)|*.png|'
1993            'Zip archive (*.zip)|*.zip|'
1994            'All files (*.*)|*.*',
1995            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
1996        try:
1997            if dlg.ShowModal() == wx.ID_OK:
1998                imagefiles = dlg.GetPaths()
1999                imagefiles.sort()
2000                for imagefile in imagefiles:
2001                    # if a zip file, open and extract
2002                    if os.path.splitext(imagefile)[1].lower() == '.zip':
2003                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
2004                        if extractedfile is not None and extractedfile != imagefile:
2005                            imagefile = extractedfile
2006                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
2007                    if Comments:
2008                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
2009                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2010                        Imax = np.amax(Image)
2011                        Imin = max(0.0,np.amin(Image))          #force positive
2012                        if self.imageDefault:
2013                            Data = copy.copy(self.imageDefault)
2014                            Data['showLines'] = True
2015                            Data['ring'] = []
2016                            Data['rings'] = []
2017                            Data['cutoff'] = 10
2018                            Data['pixLimit'] = 20
2019                            Data['edgemin'] = 100000000
2020                            Data['calibdmin'] = 0.5
2021                            Data['calibskip'] = 0
2022                            Data['ellipses'] = []
2023                            Data['calibrant'] = ''
2024                            Data['GonioAngles'] = [0.,0.,0.]
2025                            Data['DetDepth'] = 0.
2026                            Data['DetDepthRef'] = False
2027                        else:
2028                            Data['type'] = 'PWDR'
2029                            Data['color'] = 'Paired'
2030                            Data['tilt'] = 0.0
2031                            Data['rotation'] = 0.0
2032                            Data['showLines'] = False
2033                            Data['ring'] = []
2034                            Data['rings'] = []
2035                            Data['cutoff'] = 10
2036                            Data['pixLimit'] = 20
2037                            Data['calibdmin'] = 0.5
2038                            Data['calibskip'] = 0
2039                            Data['edgemin'] = 100000000
2040                            Data['ellipses'] = []
2041                            Data['GonioAngles'] = [0.,0.,0.]
2042                            Data['DetDepth'] = 0.
2043                            Data['DetDepthRef'] = False
2044                            Data['calibrant'] = ''
2045                            Data['IOtth'] = [2.0,5.0]
2046                            Data['LRazimuth'] = [135,225]
2047                            Data['azmthOff'] = 0.0
2048                            Data['outChannels'] = 2500
2049                            Data['outAzimuths'] = 1
2050                            Data['centerAzm'] = False
2051                            Data['fullIntegrate'] = False
2052                            Data['setRings'] = False
2053                            Data['background image'] = ['',-1.0]                           
2054                            Data['dark image'] = ['',-1.0]
2055                        Data['setDefault'] = False
2056                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2057                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
2058                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2059                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2060                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2061                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'strain':np.zeros((3,3))})
2062                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
2063                        self.PickId = Id
2064                        self.Image = Id
2065                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2066                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
2067        finally:
2068            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2069            os.chdir(path)
2070            dlg.Destroy()
2071
2072    def CheckNotebook(self):
2073        '''Make sure the data tree has the minimally expected controls.
2074        (BHT) correct?
2075        '''
2076        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2077            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2078            self.PatternTree.SetItemPyData(sub,[''])
2079        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2080            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2081            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2082        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2083            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2084            self.PatternTree.SetItemPyData(sub,{})
2085        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2086            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2087            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2088        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2089            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2090            self.PatternTree.SetItemPyData(sub,{})
2091        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2092            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2093            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2094                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2095               
2096    class CopyDialog(wx.Dialog):
2097        '''Creates a dialog for copying control settings between
2098        data tree items'''
2099        def __init__(self,parent,title,text,data):
2100            wx.Dialog.__init__(self,parent,-1,title, 
2101                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2102            self.data = data
2103            panel = wx.Panel(self)
2104            mainSizer = wx.BoxSizer(wx.VERTICAL)
2105            topLabl = wx.StaticText(panel,-1,text)
2106            mainSizer.Add((10,10),1)
2107            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2108            mainSizer.Add((10,10),1)
2109            ncols = len(data)/40+1
2110            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=ncols,hgap=2,vgap=2)
2111            for id,item in enumerate(self.data):
2112                ckbox = wx.CheckBox(panel,id,item[1])
2113                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2114                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2115            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2116            OkBtn = wx.Button(panel,-1,"Ok")
2117            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2118            cancelBtn = wx.Button(panel,-1,"Cancel")
2119            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2120            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2121            btnSizer.Add((20,20),1)
2122            btnSizer.Add(OkBtn)
2123            btnSizer.Add((20,20),1)
2124            btnSizer.Add(cancelBtn)
2125            btnSizer.Add((20,20),1)
2126           
2127            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2128            panel.SetSizer(mainSizer)
2129            panel.Fit()
2130            self.Fit()
2131       
2132        def OnCopyChange(self,event):
2133            id = event.GetId()
2134            self.data[id][0] = self.FindWindowById(id).GetValue()       
2135           
2136        def OnOk(self,event):
2137            parent = self.GetParent()
2138            parent.Raise()
2139            self.EndModal(wx.ID_OK)             
2140           
2141        def OnCancel(self,event):
2142            parent = self.GetParent()
2143            parent.Raise()
2144            self.EndModal(wx.ID_CANCEL)             
2145           
2146        def GetData(self):
2147            return self.data
2148       
2149    class SumDialog(wx.Dialog):
2150        'Allows user to supply scale factor(s) when summing data'
2151        def __init__(self,parent,title,text,dataType,data):
2152            wx.Dialog.__init__(self,parent,-1,title, 
2153                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2154            self.data = data
2155            panel = wx.Panel(self)
2156            mainSizer = wx.BoxSizer(wx.VERTICAL)
2157            topLabl = wx.StaticText(panel,-1,text)
2158            mainSizer.Add((10,10),1)
2159            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2160            mainSizer.Add((10,10),1)
2161            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
2162            for id,item in enumerate(self.data[:-1]):
2163                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2164                name.SetEditable(False)
2165                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2166                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2167                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2168                dataGridSizer.Add(scale,0,wx.LEFT,10)
2169                dataGridSizer.Add(name,0,wx.RIGHT,10)
2170            if dataType:
2171                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2172                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2173                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2174                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2175                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2176                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2177            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2178            OkBtn = wx.Button(panel,-1,"Ok")
2179            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2180            cancelBtn = wx.Button(panel,-1,"Cancel")
2181            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2182            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2183            btnSizer.Add((20,20),1)
2184            btnSizer.Add(OkBtn)
2185            btnSizer.Add((20,20),1)
2186            btnSizer.Add(cancelBtn)
2187            btnSizer.Add((20,20),1)
2188           
2189            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2190            panel.SetSizer(mainSizer)
2191            panel.Fit()
2192            self.Fit()
2193
2194        def OnScaleChange(self,event):
2195            id = event.GetId()
2196            value = self.FindWindowById(id).GetValue()
2197            try:
2198                self.data[id][0] = float(value)
2199                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2200            except ValueError:
2201                if value and '-' not in value[0]:
2202                    print 'bad input - numbers only'
2203                    self.FindWindowById(id).SetValue('0.000')
2204           
2205        def OnNameChange(self,event):
2206            self.data[-1] = self.name.GetValue() 
2207           
2208        def OnOk(self,event):
2209            parent = self.GetParent()
2210            parent.Raise()
2211            self.EndModal(wx.ID_OK)             
2212           
2213        def OnCancel(self,event):
2214            parent = self.GetParent()
2215            parent.Raise()
2216            self.EndModal(wx.ID_CANCEL)             
2217           
2218        def GetData(self):
2219            return self.data
2220                       
2221    def OnPwdrSum(self,event):
2222        'Sum together powder data(?)'
2223        TextList = []
2224        DataList = []
2225        SumList = []
2226        Names = []
2227        Inst = None
2228        SumItemList = []
2229        Comments = ['Sum equals: \n']
2230        if self.PatternTree.GetCount():
2231            item, cookie = self.PatternTree.GetFirstChild(self.root)
2232            while item:
2233                name = self.PatternTree.GetItemText(item)
2234                Names.append(name)
2235                if 'PWDR' in name:
2236                    TextList.append([0.0,name])
2237                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2238                    if not Inst:
2239                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2240                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2241            if len(TextList) < 2:
2242                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2243                return
2244            TextList.append('default_sum_name')               
2245            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2246            try:
2247                if dlg.ShowModal() == wx.ID_OK:
2248                    lenX = 0
2249                    Xminmax = [0,0]
2250                    Xsum = []
2251                    Ysum = []
2252                    Vsum = []
2253                    result = dlg.GetData()
2254                    for i,item in enumerate(result[:-1]):
2255                        scale,name = item
2256                        data = DataList[i]
2257                        if scale:
2258                            Comments.append("%10.3f %s" % (scale,' * '+name))
2259                            x,y,w,yc,yb,yd = data   #numpy arrays!
2260                            v = 1./w
2261                            if lenX:
2262                                if lenX != len(x):
2263                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2264                                        '\nExpected:'+str(lenX)+ \
2265                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2266                                    return
2267                            else:
2268                                lenX = len(x)
2269                            if Xminmax[1]:
2270                                if Xminmax != [x[0],x[-1]]:
2271                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2272                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2273                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2274                                    return
2275                                else:
2276                                    for j,yi in enumerate(y):
2277                                         Ysum[j] += scale*yi
2278                                         Vsum[j] += abs(scale)*v[j]
2279                            else:
2280                                Xminmax = [x[0],x[-1]]
2281                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2282                                for j,yi in enumerate(y):
2283                                    Xsum.append(x[j])
2284                                    Ysum.append(scale*yi)
2285                                    Vsum.append(abs(scale*v[j]))
2286                    Wsum = 1./np.array(Vsum)
2287                    outname = 'PWDR '+result[-1]
2288                    Id = 0
2289                    if outname in Names:
2290                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2291                        try:
2292                            if dlg2.ShowModal() == wx.ID_OK:
2293                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2294                                self.PatternTree.Delete(Id)
2295                        finally:
2296                            dlg2.Destroy()
2297                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2298                    if Id:
2299                        Sample = G2pdG.SetDefaultSample()
2300                        valuesdict = {
2301                            'wtFactor':1.0,
2302                            'Dummy':False,
2303                            'ranId':ran.randint(0,sys.maxint),
2304                            }
2305                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2306                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2307                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2308                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2309                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2310                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2311                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2312                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2313                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
2314                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
2315                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2316                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2317                        self.PatternTree.SelectItem(Id)
2318                        self.PatternTree.Expand(Id)
2319            finally:
2320                dlg.Destroy()
2321
2322    def OnImageSum(self,event):
2323        'Sum together image data(?)'
2324        TextList = []
2325        DataList = []
2326        SumList = []
2327        Names = []
2328        Inst = []
2329        SumItemList = []
2330        Comments = ['Sum equals: \n']
2331        if self.PatternTree.GetCount():
2332            item, cookie = self.PatternTree.GetFirstChild(self.root)
2333            while item:
2334                name = self.PatternTree.GetItemText(item)
2335                Names.append(name)
2336                if 'IMG' in name:
2337                    TextList.append([0.0,name])
2338                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2339                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2340                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2341            if len(TextList) < 2:
2342                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2343                return
2344            TextList.append('default_sum_name')               
2345            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2346            try:
2347                if dlg.ShowModal() == wx.ID_OK:
2348                    imSize = 0
2349                    result = dlg.GetData()
2350                    First = True
2351                    Found = False
2352                    for i,item in enumerate(result[:-1]):
2353                        scale,name = item
2354                        data = DataList[i]
2355                        if scale:
2356                            Found = True                               
2357                            Comments.append("%10.3f %s" % (scale,' * '+name))
2358                            Npix,imagefile = data
2359                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2360                            if First:
2361                                newImage = np.zeros_like(image)
2362                                First = False
2363                            if imSize:
2364                                if imSize != Npix:
2365                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2366                                        '\nExpected:'+str(imSize)+ \
2367                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2368                                    return
2369                                newImage = newImage+scale*image
2370                            else:
2371                                imSize = Npix
2372                                newImage = newImage+scale*image
2373                            del(image)
2374                    if not Found:
2375                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2376                        return
2377                       
2378                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2379                    outname = 'IMG '+result[-1]
2380                    Id = 0
2381                    if outname in Names:
2382                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2383                        try:
2384                            if dlg2.ShowModal() == wx.ID_OK:
2385                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2386                        finally:
2387                            dlg2.Destroy()
2388                    else:
2389                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2390                    if Id:
2391                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2392                            'G2img files (*.G2img)|*.G2img', 
2393                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2394                        if dlg.ShowModal() == wx.ID_OK:
2395                            newimagefile = dlg.GetPath()
2396                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2397                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2398                            Imax = np.amax(newImage)
2399                            Imin = np.amin(newImage)
2400                            newImage = []
2401                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2402                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2403                        del(newImage)
2404                        if self.imageDefault:
2405                            Data = copy.copy(self.imageDefault)
2406                        Data['showLines'] = True
2407                        Data['ring'] = []
2408                        Data['rings'] = []
2409                        Data['cutoff'] = 10
2410                        Data['pixLimit'] = 20
2411                        Data['ellipses'] = []
2412                        Data['calibrant'] = ''
2413                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2414                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2415                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2416                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2417                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),{})
2418                        self.PatternTree.SelectItem(Id)
2419                        self.PatternTree.Expand(Id)
2420                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2421                        self.Image = self.PickId
2422            finally:
2423                dlg.Destroy()
2424                     
2425    def OnAddPhase(self,event):
2426        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2427        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2428            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2429        else:
2430            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2431        PhaseName = ''
2432        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2433            style=wx.OK)
2434        if dlg.ShowModal() == wx.ID_OK:
2435            PhaseName = dlg.GetValue()
2436        dlg.Destroy()
2437        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2438        E,SGData = G2spc.SpcGroup('P 1')
2439        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2440       
2441    def OnDeletePhase(self,event):
2442        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2443        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2444        if self.dataFrame:
2445            self.dataFrame.Clear() 
2446        TextList = []
2447        DelList = []
2448        DelItemList = []
2449        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2450            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2451        else:
2452            return
2453        if sub:
2454            item, cookie = self.PatternTree.GetFirstChild(sub)
2455            while item:
2456                TextList.append(self.PatternTree.GetItemText(item))
2457                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2458            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2459            try:
2460                if dlg.ShowModal() == wx.ID_OK:
2461                    result = dlg.GetSelections()
2462                    for i in result: DelList.append([i,TextList[i]])
2463                    item, cookie = self.PatternTree.GetFirstChild(sub)
2464                    i = 0
2465                    while item:
2466                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2467                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2468                        i += 1
2469                    for item in DelItemList:
2470                        name = self.PatternTree.GetItemText(item)
2471                        self.PatternTree.Delete(item)
2472                        self.G2plotNB.Delete(name)
2473                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2474                    while item:
2475                        name = self.PatternTree.GetItemText(item)
2476                        if 'PWDR' in name:
2477                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2478                            refList = self.PatternTree.GetItemPyData(Id)
2479                            for i,item in DelList:
2480                                del(refList[item])
2481                            self.PatternTree.SetItemPyData(Id,refList)
2482                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2483            finally:
2484                dlg.Destroy()
2485               
2486    def OnRenameData(self,event):
2487        'Renames an existing phase. Called by Data/Rename Phase menu'
2488        name = self.PatternTree.GetItemText(self.PickId)     
2489        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2490            dataType = name[:name.index(' ')+1]                 #includes the ' '
2491            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2492                defaultValue=name[name.index(' ')+1:])
2493            try:
2494                if dlg.ShowModal() == wx.ID_OK:
2495                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2496            finally:
2497                dlg.Destroy()
2498       
2499    def GetFileList(self,fileType,skip=None):        #potentially useful?
2500        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2501        fileList = []
2502        Source = ''
2503        id, cookie = self.PatternTree.GetFirstChild(self.root)
2504        while id:
2505            name = self.PatternTree.GetItemText(id)
2506            if fileType in name:
2507                if id == skip:
2508                    Source = name
2509                else:
2510                    fileList.append([False,name,id])
2511            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2512        if skip:
2513            return fileList,Source
2514        else:
2515            return fileList
2516           
2517    def OnDataDelete(self, event):
2518        '''Delete one or more histograms from data tree. Called by the
2519        Data/DeleteData menu
2520        '''
2521        TextList = ['All Data']
2522        DelList = []
2523        DelItemList = []
2524        ifPWDR = False
2525        ifSASD = False
2526        ifIMG = False
2527        ifHKLF = False
2528        ifPDF = False
2529        if self.PatternTree.GetCount():
2530            item, cookie = self.PatternTree.GetFirstChild(self.root)
2531            while item:
2532                name = self.PatternTree.GetItemText(item)
2533                if name not in ['Notebook','Controls','Covariance','Constraints',
2534                    'Restraints','Phases','Rigid bodies']:
2535                    if 'PWDR' in name: ifPWDR = True
2536                    if 'SASD' in name: ifSASD = True
2537                    if 'IMG' in name: ifIMG = True
2538                    if 'HKLF' in name: ifHKLF = True
2539                    if 'PDF' in name: ifPDF = True
2540                    TextList.append(name)
2541                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2542            if ifPWDR: TextList.insert(1,'All PWDR')
2543            if ifSASD: TextList.insert(1,'All SASD')
2544            if ifIMG: TextList.insert(1,'All IMG')
2545            if ifHKLF: TextList.insert(1,'All HKLF')
2546            if ifPDF: TextList.insert(1,'All PDF')               
2547            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2548            try:
2549                if dlg.ShowModal() == wx.ID_OK:
2550                    result = dlg.GetSelections()
2551                    for i in result: DelList.append(TextList[i])
2552                    if 'All Data' in DelList:
2553                        DelList = [item for item in TextList if item[:3] != 'All']
2554                    elif 'All PWDR' in DelList:
2555                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2556                    elif 'All SASD' in DelList:
2557                        DelList = [item for item in TextList if item[:4] == 'SASD']
2558                    elif 'All IMG' in DelList:
2559                        DelList = [item for item in TextList if item[:3] == 'IMG']
2560                    elif 'All HKLF' in DelList:
2561                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2562                    elif 'All PDF' in DelList:
2563                        DelList = [item for item in TextList if item[:3] == 'PDF']
2564                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2565                    while item:
2566                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2567                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2568                    for item in DelItemList:
2569                        self.PatternTree.Delete(item)
2570                    self.PickId = 0
2571                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2572            finally:
2573                dlg.Destroy()
2574
2575    def OnFileOpen(self, event, filename=None):
2576        '''Reads in a GSAS-II .gpx project file in response to the
2577        File/Open Project menu button
2578        '''
2579        result = wx.ID_OK
2580        Id = 0
2581        if self.PatternTree.GetChildrenCount(self.root,False):
2582            if self.dataFrame:
2583                self.dataFrame.Clear() 
2584            dlg = wx.MessageDialog(
2585                self,
2586                'Do you want to overwrite the current project? '
2587                'Any unsaved changes will be lost. Press OK to continue.',
2588                'Overwrite?',  wx.OK | wx.CANCEL)
2589            try:
2590                result = dlg.ShowModal()
2591                if result == wx.ID_OK:
2592                    self.PatternTree.DeleteChildren(self.root)
2593                    self.GSASprojectfile = ''
2594                    if self.HKL: self.HKL = []
2595                    if self.G2plotNB.plotList:
2596                        self.G2plotNB.clear()
2597            finally:
2598                dlg.Destroy()
2599        if result != wx.ID_OK: return
2600
2601        if not filename:
2602            if self.dataDisplay: self.dataDisplay.Destroy()
2603            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2604                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2605            try:
2606                if dlg.ShowModal() != wx.ID_OK: return
2607                self.GSASprojectfile = dlg.GetPath()
2608                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2609                self.dirname = dlg.GetDirectory()
2610            finally:
2611                dlg.Destroy()
2612        else:
2613            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2614            self.dirname = os.path.split(filename)[0]
2615
2616        G2IO.ProjFileOpen(self)
2617        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2618        self.PatternTree.Expand(self.root)
2619        self.HKL = []
2620        item, cookie = self.PatternTree.GetFirstChild(self.root)
2621        while item and not Id:
2622            name = self.PatternTree.GetItemText(item)
2623            if name[:4] in ['PWDR','HKLF','IMG ','PDF ']:
2624                Id = item
2625            elif name == 'Controls':
2626                data = self.PatternTree.GetItemPyData(item)
2627                if data:
2628                    for item in self.Refine: item.Enable(True)
2629                    for item in self.SeqRefine: item.Enable(True)
2630            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2631        if Id:
2632            self.PatternTree.SelectItem(Id)
2633        self.CheckNotebook()
2634        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2635
2636    def OnFileClose(self, event):
2637        '''Clears the data tree in response to the
2638        File/New Project menu button. User is given option to save
2639        the project.
2640        '''
2641        if self.dataFrame:
2642            self.dataFrame.Clear()
2643            self.dataFrame.SetLabel('GSAS-II data display') 
2644        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2645        try:
2646            result = dlg.ShowModal()
2647            if result == wx.ID_OK:
2648                self.OnFileSaveMenu(event)
2649            if result != wx.ID_CANCEL:
2650                self.GSASprojectfile = ''
2651                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2652                self.PatternTree.DeleteChildren(self.root)
2653                if self.HKL: self.HKL = []
2654                if self.G2plotNB.plotList:
2655                    self.G2plotNB.clear()
2656        finally:
2657            dlg.Destroy()
2658
2659    def OnFileSave(self, event):
2660        '''Save the current project in response to the
2661        File/Save Project menu button
2662        '''
2663       
2664        if self.GSASprojectfile: 
2665            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2666            G2IO.ProjFileSave(self)
2667        else:
2668            self.OnFileSaveas(event)
2669
2670    def OnFileSaveas(self, event):
2671        '''Save the current project in response to the
2672        File/Save as menu button
2673        '''
2674        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2675            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2676        try:
2677            if dlg.ShowModal() == wx.ID_OK:
2678                self.GSASprojectfile = dlg.GetPath()
2679                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2680                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2681                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2682                G2IO.ProjFileSave(self)
2683                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2684        finally:
2685            dlg.Destroy()
2686
2687    def ExitMain(self, event):
2688        '''Called if the main window is closed'''
2689        if self.undofile:
2690            os.remove(self.undofile)
2691        sys.exit()
2692       
2693    def OnFileExit(self, event):
2694        '''Called in response to the File/Quit menu button'''
2695        if self.dataFrame:
2696            self.dataFrame.Clear() 
2697            self.dataFrame.Destroy()
2698        self.Close()
2699       
2700    def OnExportPatterns(self,event):
2701        names = ['All']
2702        exports = []
2703        item, cookie = self.PatternTree.GetFirstChild(self.root)
2704        while item:
2705            name = self.PatternTree.GetItemText(item)
2706            if 'PWDR' in name:
2707                names.append(name)
2708            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2709        if names:
2710            dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2711            if dlg.ShowModal() == wx.ID_OK:
2712                sel = dlg.GetSelections()
2713                if sel[0] == 0:
2714                    exports = names[1:]
2715                else:
2716                    for x in sel:
2717                        exports.append(names[x])
2718            dlg.Destroy()
2719        if exports:
2720            dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '', 
2721                'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2722                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2723            try:
2724                if dlg.ShowModal() == wx.ID_OK:
2725                    powderfile = dlg.GetPath()
2726                    powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2727                    if 'fxye' in powderfile:
2728                        G2IO.powderFxyeSave(self,exports,powderfile)
2729                    else:       #just xye
2730                        G2IO.powderXyeSave(self,exports,powderfile)
2731            finally:
2732                dlg.Destroy()
2733       
2734    def OnExportPeakList(self,event):
2735        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2736            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2737        try:
2738            if dlg.ShowModal() == wx.ID_OK:
2739                self.peaklistfile = dlg.GetPath()
2740                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2741                file = open(self.peaklistfile,'w')               
2742                item, cookie = self.PatternTree.GetFirstChild(self.root)
2743                while item:
2744                    name = self.PatternTree.GetItemText(item)
2745                    if 'PWDR' in name:
2746                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2747                        while item2:
2748                            name2 = self.PatternTree.GetItemText(item2)
2749                            if name2 == 'Peak List':
2750                                peaks = self.PatternTree.GetItemPyData(item2)
2751                                file.write("%s \n" % (name+' Peak List'))               
2752                                for peak in peaks:
2753                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2754                                        (peak[0],peak[2],peak[4],peak[6]))
2755                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2756                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2757                file.close()
2758        finally:
2759            dlg.Destroy()
2760       
2761    def OnExportHKL(self,event):
2762        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2763            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2764        try:
2765            if dlg.ShowModal() == wx.ID_OK:
2766                self.peaklistfile = dlg.GetPath()
2767                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2768                file = open(self.peaklistfile,'w')               
2769                item, cookie = self.PatternTree.GetFirstChild(self.root)
2770                while item:
2771                    name = self.PatternTree.GetItemText(item)
2772                    if 'PWDR' in name:
2773                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2774                        while item2:
2775                            name2 = self.PatternTree.GetItemText(item2)
2776                            if name2 == 'Reflection Lists':
2777                                data = self.PatternTree.GetItemPyData(item2)
2778                                phases = data.keys()
2779                                for phase in phases:
2780                                    peaks = data[phase]
2781                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2782                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2783                                    for peak in peaks:
2784                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2785                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2786                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2787                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2788                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2789                file.close()
2790        finally:
2791            dlg.Destroy()
2792       
2793    def OnExportPDF(self,event):
2794        #need S(Q) and G(R) to be saved here - probably best from selection?
2795        names = ['All']
2796        exports = []
2797        item, cookie = self.PatternTree.GetFirstChild(self.root)
2798        while item:
2799            name = self.PatternTree.GetItemText(item)
2800            if 'PDF' in name:
2801                names.append(name)
2802            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2803        if names:
2804            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2805            if dlg.ShowModal() == wx.ID_OK:
2806                sel = dlg.GetSelections()
2807                if sel[0] == 0:
2808                    exports = names[1:]
2809                else:
2810                    for x in sel:
2811                        exports.append(names[x])
2812            dlg.Destroy()
2813        if exports:
2814            G2IO.PDFSave(self,exports)
2815       
2816    def OnMakePDFs(self,event):
2817        '''Calculates PDFs
2818        '''
2819        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2820        TextList = ['All PWDR']
2821        PDFlist = []
2822        Names = []
2823        if self.PatternTree.GetCount():
2824            id, cookie = self.PatternTree.GetFirstChild(self.root)
2825            while id:
2826                name = self.PatternTree.GetItemText(id)
2827                Names.append(name)
2828                if 'PWDR' in name:
2829                    TextList.append(name)
2830                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2831            if len(TextList) == 1:
2832                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2833                return
2834            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2835            try:
2836                if dlg.ShowModal() == wx.ID_OK:
2837                    result = dlg.GetSelections()
2838                    for i in result: PDFlist.append(TextList[i])
2839                    if 0 in result:
2840                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2841                    for item in PDFlist:
2842                        PWDRname = item[4:]
2843                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2844                        Data = {
2845                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2846                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2847                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2848                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2849                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2850                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2851                            'Lorch':True,}
2852                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2853                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2854                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2855                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2856                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2857                for item in self.ExportPDF: item.Enable(True)
2858            finally:
2859                dlg.Destroy()
2860               
2861    def GetPWDRdatafromTree(self,PWDRname):
2862        ''' Returns powder data from GSASII tree
2863
2864        :param str PWDRname: a powder histogram name as obtained from
2865          :meth:`GSASIIstruct.GetHistogramNames`
2866
2867        :returns: PWDRdata = powder data dictionary with
2868          Powder data arrays, Limits, Instrument Parameters,
2869          Sample Parameters           
2870        '''
2871        PWDRdata = {}
2872        try:
2873            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2874        except ValueError:
2875            PWDRdata['wtFactor'] = 1.0
2876        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2877        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2878        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2879        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2880        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2881        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2882        if 'ranId' not in PWDRdata:  # patch, add a random Id
2883            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
2884        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
2885            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
2886        return PWDRdata
2887
2888    def GetHKLFdatafromTree(self,HKLFname):
2889        ''' Returns single crystal data from GSASII tree
2890
2891        :param str HKLFname: a single crystal histogram name as obtained
2892          from
2893          :meth:`GSASIIstruct.GetHistogramNames`
2894
2895        :returns: HKLFdata = single crystal data list of reflections
2896
2897        '''
2898        HKLFdata = {}
2899        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2900#        try:
2901#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2902#        except ValueError:
2903#            HKLFdata['wtFactor'] = 1.0
2904        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2905        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2906        return HKLFdata
2907       
2908    def GetPhaseData(self):
2909        '''Returns a dict with defined phases.
2910        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
2911        get same info from GPX file.
2912        '''
2913        phaseData = {}
2914        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2915            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2916        else:
2917            print 'no phases found in GetPhaseData'
2918            sub = None
2919        if sub:
2920            item, cookie = self.PatternTree.GetFirstChild(sub)
2921            while item:
2922                phaseName = self.PatternTree.GetItemText(item)
2923                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2924                if 'ranId' not in phaseData[phaseName]:
2925                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2926                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2927        return phaseData
2928
2929    def GetPhaseInfofromTree(self):
2930        '''Get the phase names and their rId values,
2931        also the histograms used in each phase.
2932
2933        :returns: (phaseRIdList, usedHistograms) where
2934
2935          * phaseRIdList is a list of random Id values for each phase
2936          * usedHistograms is a dict where the keys are the phase names
2937            and the values for each key are a list of the histogram names
2938            used in each phase.
2939        '''
2940        phaseRIdList = []
2941        usedHistograms = {}
2942        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2943        if sub:
2944            item, cookie = self.PatternTree.GetFirstChild(sub)
2945            while item:
2946                phaseName = self.PatternTree.GetItemText(item)
2947                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2948                if ranId: phaseRIdList.append(ranId)
2949                data = self.PatternTree.GetItemPyData(item)
2950                UseList = data['Histograms']
2951                usedHistograms[phaseName] = UseList.keys()
2952                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2953        return phaseRIdList,usedHistograms
2954
2955    def GetPhaseNames(self):
2956        '''Returns a list of defined phases.
2957        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
2958        get same info from GPX file.
2959        '''
2960        phaseNames = []
2961        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2962            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2963        else:
2964            print 'no phases found in GetPhaseNames'
2965            sub = None
2966        if sub:
2967            item, cookie = self.PatternTree.GetFirstChild(sub)
2968            while item:
2969                phase = self.PatternTree.GetItemText(item)
2970                phaseNames.append(phase)
2971                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2972        return phaseNames
2973   
2974    def GetHistogramNames(self,hType):
2975        """ Returns a list of histogram names found in the GSASII data tree
2976        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
2977        get same info from GPX file.
2978       
2979        :param str hType: list of histogram types
2980        :return: list of histogram names
2981       
2982        """
2983        HistogramNames = []
2984        if self.PatternTree.GetCount():
2985            item, cookie = self.PatternTree.GetFirstChild(self.root)
2986            while item:
2987                name = self.PatternTree.GetItemText(item)
2988                if name[:4] in hType:
2989                    HistogramNames.append(name)       
2990                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2991
2992        return HistogramNames
2993
2994                   
2995    def GetUsedHistogramsAndPhasesfromTree(self):
2996        ''' Returns all histograms that are found in any phase
2997        and any phase that uses a histogram.
2998        This also assigns numbers to used phases and histograms by the
2999        order they appear in the file.
3000        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3001        get same info from GPX file.
3002
3003        :returns: (Histograms,Phases)
3004
3005            * Histograms = dictionary of histograms as {name:data,...}
3006            * Phases = dictionary of phases that use histograms
3007        '''
3008        Histograms = {}
3009        Phases = {}
3010        phaseNames = self.GetPhaseNames()
3011        phaseData = self.GetPhaseData()
3012        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3013
3014        for phase in phaseData:
3015            Phase = phaseData[phase]
3016            pId = phaseNames.index(phase)
3017            Phase['pId'] = pId
3018            if Phase['Histograms']:
3019                if phase not in Phases:
3020                    Phases[phase] = Phase
3021                for hist in Phase['Histograms']:
3022                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3023                        Phase['Histograms'][hist]['Use'] = True         
3024                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3025                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3026                        if item:
3027                            if 'PWDR' in hist[:4]: 
3028                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3029                            elif 'HKLF' in hist[:4]:
3030                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3031                            hId = histoList.index(hist)
3032                            Histograms[hist]['hId'] = hId
3033                        else: # would happen if a referenced histogram were renamed or deleted
3034                            print('For phase "'+str(phase)+
3035                                  '" unresolved reference to histogram "'+str(hist)+'"')
3036        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3037        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3038        return Histograms,Phases
3039       
3040    def MakeLSParmDict(self):
3041        '''Load all parameters used for computation from the tree into a
3042        dict of paired values [value, refine flag]. Note that this is
3043        different than the parmDict used in the refinement, which only has
3044        values.
3045
3046        Note that similar things are done in
3047        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3048        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3049        a GPX file).
3050
3051        :returns: (parmDict,varyList) where:
3052
3053         * parmDict is a dict with values and refinement flags
3054           for each parameter and
3055         * varyList is a list of variables (refined parameters).
3056        '''
3057        parmDict = {}
3058        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3059        for phase in Phases:
3060            if 'pId' not in Phases[phase]:
3061                self.ErrorDialog('View parameter error','You must run least squares at least once')
3062                raise Exception,'No pId for phase '+str(phase)
3063        rigidbodyDict = self.PatternTree.GetItemPyData(   
3064            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3065        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3066        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3067        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3068        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3069        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3070        varyList = rbVary+phaseVary+hapVary+histVary
3071        parmDict.update(rbDict)
3072        parmDict.update(phaseDict)
3073        parmDict.update(hapDict)
3074        parmDict.update(histDict)
3075        for parm in parmDict:
3076            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3077                'Omega','Chi','Phi','nDebye','nPeaks']:
3078                parmDict[parm] = [parmDict[parm],'-']
3079            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3080                parmDict[parm] = [parmDict[parm],'-']
3081            elif parm in varyList:
3082                parmDict[parm] = [parmDict[parm],'T']
3083            else:
3084                parmDict[parm] = [parmDict[parm],'F']
3085        # for i in parmDict: print i,'\t',parmDict[i]
3086        # fl = open('parmDict.dat','wb')
3087        # import cPickle
3088        # cPickle.dump(parmDict,fl,1)
3089        # fl.close()
3090        return parmDict,varyList
3091
3092    def ShowLSParms(self,event):
3093        '''Displays a window showing all parameters in the refinement.
3094        Called from the Calculate/View LS Parms menu.
3095        '''
3096        parmDict,varyList = self.MakeLSParmDict()
3097        parmValDict = {}
3098        for i in parmDict:
3099            parmValDict[i] = parmDict[i][0]
3100           
3101        reqVaryList = tuple(varyList) # save requested variables
3102        try:
3103            # process constraints
3104            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3105            Constraints = self.PatternTree.GetItemPyData(sub)
3106            constList = []
3107            for item in Constraints:
3108                if item.startswith('_'): continue
3109                constList += Constraints[item]
3110            G2mv.InitVars()
3111            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3112            groups,parmlist = G2mv.GroupConstraints(constrDict)
3113            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList)
3114            G2mv.Map2Dict(parmValDict,varyList)
3115        except:
3116            pass
3117        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3118        dlg.ShowModal()
3119        dlg.Destroy()
3120       
3121    def OnRefine(self,event):
3122        '''Perform a refinement.
3123        Called from the Calculate/Refine menu.
3124        '''       
3125        self.OnFileSave(event)
3126        # check that constraints are OK here
3127        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3128        if errmsg:
3129            print('Error in constraints:\n'+errmsg+
3130                  '\nRefinement not possible')
3131            self.ErrorDialog('Constraint Error',
3132                             'Error in constraints:\n'+errmsg+
3133                             '\nRefinement not possible')
3134            return
3135        if warnmsg:
3136            print('Conflict between refinment flag settings and constraints:\n'+
3137                  warnmsg+'\nRefinement not possible')
3138            self.ErrorDialog('Refinement Flag Error',
3139                             'Conflict between refinment flag settings and constraints:\n'+
3140                             warnmsg+
3141                             '\nRefinement not possible')
3142            return
3143        #works - but it'd be better if it could restore plots
3144        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3145            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3146        screenSize = wx.ClientDisplayRect()
3147        Size = dlg.GetSize()
3148        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3149        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3150        dlg.SetSize(Size)
3151        Rw = 100.00
3152        try:
3153            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3154        finally:
3155            dlg.Destroy()
3156        oldId =  self.PatternTree.GetSelection()
3157        oldName = self.PatternTree.GetItemText(oldId)
3158        parentId = self.PatternTree.GetItemParent(oldId)
3159        parentName = ''
3160        if parentId:
3161            parentName = self.PatternTree.GetItemText(parentId)
3162        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3163        try:
3164            if dlg.ShowModal() == wx.ID_OK:
3165                Id = 0
3166                self.PatternTree.DeleteChildren(self.root)
3167                if self.HKL: self.HKL = []
3168                if self.G2plotNB.plotList:
3169                    self.G2plotNB.clear()
3170                G2IO.ProjFileOpen(self)
3171                item, cookie = self.PatternTree.GetFirstChild(self.root)
3172                while item and not Id:
3173                    name = self.PatternTree.GetItemText(item)
3174                    if name[:4] in ['PWDR','HKLF']:
3175                        Id = item
3176                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3177                if parentName:
3178                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3179                    if parentId:
3180                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3181                    else:
3182                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3183                    self.PatternTree.SelectItem(itemId)
3184                elif Id:
3185                    self.PatternTree.SelectItem(Id)
3186        finally:
3187            dlg.Destroy()
3188
3189    def OnSeqRefine(self,event):
3190        '''Perform a sequential refinement.
3191        Called from the Calculate/Sequential refine menu.
3192        '''       
3193        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3194        if not Id:
3195            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3196            self.PatternTree.SetItemPyData(Id,{})           
3197        self.OnFileSave(event)
3198        # check that constraints are OK here
3199        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3200        if errmsg:
3201            print('Error in constraints:\n'+errmsg+
3202                  '\nRefinement not possible')
3203            self.ErrorDialog('Constraint Error',
3204                             'Error in constraints:\n'+errmsg+
3205                             '\nRefinement not possible')
3206            return
3207        if warnmsg:
3208            print('Conflict between refinment flag settings and constraints:\n'+
3209                  warnmsg+'\nRefinement not possible')
3210            self.ErrorDialog('Refinement Flag Error',
3211                             'Conflict between refinment flag settings and constraints:\n'+
3212                             warnmsg+'\nRefinement not possible')
3213            return
3214        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3215            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3216        screenSize = wx.ClientDisplayRect()
3217        Size = dlg.GetSize()
3218        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3219        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3220        dlg.SetSize(Size)
3221        try:
3222            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3223        finally:
3224            dlg.Destroy()       
3225        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3226        try:
3227            if dlg.ShowModal() == wx.ID_OK:
3228                Id = 0
3229                self.PatternTree.DeleteChildren(self.root)
3230                if self.HKL: self.HKL = []
3231                if self.G2plotNB.plotList:
3232                    self.G2plotNB.clear()
3233                G2IO.ProjFileOpen(self)
3234                item, cookie = self.PatternTree.GetFirstChild(self.root)
3235                while item and not Id:
3236                    name = self.PatternTree.GetItemText(item)
3237                    if name[:4] in ['PWDR','HKLF']:
3238                        Id = item
3239                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3240                if Id:
3241                    self.PatternTree.SelectItem(Id)
3242        finally:
3243            dlg.Destroy()
3244       
3245    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3246        'Display an error message'
3247        result = None
3248        if parent is None:
3249            dlg = wx.MessageDialog(self, message, title,  wtype)
3250        else:
3251            dlg = wx.MessageDialog(parent, message, title,  wtype)
3252            dlg.CenterOnParent() # not working on Mac
3253        try:
3254            result = dlg.ShowModal()
3255        finally:
3256            dlg.Destroy()
3257        return result
3258
3259class GSASIImain(wx.App):
3260    '''Defines a wxApp for GSAS-II
3261
3262    Creates a wx frame (self.main) which contains the display of the
3263    data tree.
3264    '''
3265    def OnInit(self):
3266        '''Called automatically when the app is created.'''
3267        self.main = GSASII(None)
3268        self.main.Show()
3269        self.SetTopWindow(self.main)
3270        return True
3271    def MacOpenFile(self, filename):
3272        '''Called on Mac every time a file is dropped on the app when it is running,
3273        treat this like a File/Open project menu action.
3274        Should be ignored on other platforms
3275        '''
3276        self.main.OnFileOpen(None,filename)
3277
3278def main():
3279    '''Start up the GSAS-II application'''
3280    #application = GSASIImain() # don't redirect output, someday we
3281    # may want to do this if we can
3282    application = GSASIImain(0)
3283    if wxInspector:
3284        import wx.lib.inspection as wxeye
3285        wxeye.InspectionTool().Show()
3286
3287    #application.main.OnRefine(None)
3288    application.MainLoop()
3289   
3290if __name__ == '__main__':
3291    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.