source: trunk/GSASII.py @ 1365

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

fix atmdata.py for Rh (& other anomalous b isotopes)
fix powder imports to include old style PRCF records in GSAS iparm files
format of penalty fxn. prints

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