source: trunk/GSASII.py @ 1386

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

fix stress strain data references

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 158.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2014-06-17 19:45:48 +0000 (Tue, 17 Jun 2014) $
6# $Author: vondreele $
7# $Revision: 1386 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1386 2014-06-17 19:45:48Z 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: 1386 $")
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.SqrtPlot = False
1943        self.ErrorBars = False
1944        self.Contour = False
1945        self.Legend = False
1946        self.SinglePlot = True
1947        self.SubBack = False
1948        self.seqReverse = False
1949        self.plotView = 0
1950        self.Image = 0
1951        self.oldImagefile = ''
1952        self.ImageZ = []
1953        self.Integrate = 0
1954        self.imageDefault = {}
1955        self.Sngl = False
1956        self.ifGetRing = False
1957        self.MaskKey = ''           #trigger for making image masks
1958        self.StrainKey = ''         #ditto for new strain d-zeros
1959        self.EnablePlot = True
1960        self.Tutorials = False      #used for changing default directory
1961        arg = sys.argv
1962        if len(arg) > 1:
1963            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1964            self.dirname = os.path.dirname(arg[1])
1965            if self.dirname: os.chdir(self.dirname)
1966            try:
1967                G2IO.ProjFileOpen(self)
1968                self.PatternTree.Expand(self.root)
1969                for item in self.Refine: item.Enable(True)
1970                self.EnableSeqRefineMenu()
1971
1972            except:
1973                print 'Error opening file',arg[1]
1974
1975    def OnSize(self,event):
1976        'Called when the main window is resized. Not sure why'
1977        w,h = self.GetClientSizeTuple()
1978        self.mainPanel.SetSize(wx.Size(w,h))
1979        self.PatternTree.SetSize(wx.Size(w,h))
1980                       
1981    def OnPatternTreeSelChanged(self, event):
1982        '''Called when a data tree item is selected'''
1983        if self.TreeItemDelete:
1984            self.TreeItemDelete = False
1985        else:
1986            pltNum = self.G2plotNB.nb.GetSelection()
1987            if pltNum >= 0:                         #to avoid the startup with no plot!
1988                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1989                pltPlot = pltPage.figure
1990            item = event.GetItem()
1991            G2gd.MovePatternTreeToGrid(self,item)
1992            if self.oldFocus:
1993                self.oldFocus.SetFocus()
1994       
1995    def OnPatternTreeItemCollapsed(self, event):
1996        'Called when a tree item is collapsed'
1997        event.Skip()
1998
1999    def OnPatternTreeItemExpanded(self, event):
2000        'Called when a tree item is expanded'
2001        event.Skip()
2002       
2003    def OnPatternTreeItemDelete(self, event):
2004        'Called when a tree item is deleted -- not sure what this does'
2005        self.TreeItemDelete = True
2006
2007    def OnPatternTreeItemActivated(self, event):
2008        'Called when a tree item is activated'
2009        event.Skip()
2010       
2011    def OnPatternTreeKeyDown(self,event):
2012        'Not sure what this does'
2013        key = event.GetKeyCode()
2014        item = self.PickId
2015        if type(item) is int: return # is this the toplevel in tree?
2016        if key == wx.WXK_UP:
2017            self.oldFocus = wx.Window.FindFocus()
2018            self.PatternTree.GetPrevSibling(item)
2019        elif key == wx.WXK_DOWN:
2020            self.oldFocus = wx.Window.FindFocus()
2021            self.PatternTree.GetNextSibling(item)
2022               
2023    def OnReadPowderPeaks(self,event):
2024        'Bound to menu Data/Read Powder Peaks -- still needed?'
2025        Cuka = 1.54052
2026        self.CheckNotebook()
2027        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
2028            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
2029        try:
2030            if dlg.ShowModal() == wx.ID_OK:
2031                self.HKL = []
2032                self.powderfile = dlg.GetPath()
2033                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
2034                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2035                data = ['PKS',Cuka,0.0]
2036                names = ['Type','Lam','Zero'] 
2037                codes = [0,0,0]
2038                inst = [G2IO.makeInstDict(names,data,codes),{}]
2039                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2040                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2041                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),peaks)
2042                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2043                self.PatternTree.Expand(Id)
2044                self.PatternTree.SelectItem(Id)
2045                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2046        finally:
2047            dlg.Destroy()
2048                       
2049    def OnImageRead(self,event):
2050        'Called to read in an image in any known format'
2051        self.CheckNotebook()
2052        dlg = wx.FileDialog(
2053            self, 'Choose image files', '.', '',
2054            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.png)|'
2055            '*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.png;*.zip|'
2056            'European detector file (*.edf)|*.edf|'
2057            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2058            'MAR file (*.mar*)|*.mar*|'
2059            'GE Image (*.avg;*.sum)|*.avg;*.sum|'
2060            'ADSC Image (*.img)|*.img|'
2061            'GSAS-II Image (*.G2img)|*.G2img|'
2062            'Portable Network Graphics image (*.png)|*.png|'
2063            'Zip archive (*.zip)|*.zip|'
2064            'All files (*.*)|*.*',
2065            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
2066        try:
2067            if dlg.ShowModal() == wx.ID_OK:
2068                imagefiles = dlg.GetPaths()
2069                imagefiles.sort()
2070                for imagefile in imagefiles:
2071                    # if a zip file, open and extract
2072                    if os.path.splitext(imagefile)[1].lower() == '.zip':
2073                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
2074                        if extractedfile is not None and extractedfile != imagefile:
2075                            imagefile = extractedfile
2076                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
2077                    if Comments:
2078                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
2079                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2080                        Imax = np.amax(Image)
2081                        Imin = max(0.0,np.amin(Image))          #force positive
2082                        if self.imageDefault:
2083                            Data = copy.copy(self.imageDefault)
2084                            Data['showLines'] = True
2085                            Data['ring'] = []
2086                            Data['rings'] = []
2087                            Data['cutoff'] = 10
2088                            Data['pixLimit'] = 20
2089                            Data['edgemin'] = 100000000
2090                            Data['calibdmin'] = 0.5
2091                            Data['calibskip'] = 0
2092                            Data['ellipses'] = []
2093                            Data['calibrant'] = ''
2094                            Data['GonioAngles'] = [0.,0.,0.]
2095                            Data['DetDepth'] = 0.
2096                            Data['DetDepthRef'] = False
2097                        else:
2098                            Data['type'] = 'PWDR'
2099                            Data['color'] = 'Paired'
2100                            Data['tilt'] = 0.0
2101                            Data['rotation'] = 0.0
2102                            Data['showLines'] = False
2103                            Data['ring'] = []
2104                            Data['rings'] = []
2105                            Data['cutoff'] = 10
2106                            Data['pixLimit'] = 20
2107                            Data['calibdmin'] = 0.5
2108                            Data['calibskip'] = 0
2109                            Data['edgemin'] = 100000000
2110                            Data['ellipses'] = []
2111                            Data['GonioAngles'] = [0.,0.,0.]
2112                            Data['DetDepth'] = 0.
2113                            Data['DetDepthRef'] = False
2114                            Data['calibrant'] = ''
2115                            Data['IOtth'] = [2.0,5.0]
2116                            Data['LRazimuth'] = [135,225]
2117                            Data['azmthOff'] = 0.0
2118                            Data['outChannels'] = 2500
2119                            Data['outAzimuths'] = 1
2120                            Data['centerAzm'] = False
2121                            Data['fullIntegrate'] = False
2122                            Data['setRings'] = False
2123                            Data['background image'] = ['',-1.0]                           
2124                            Data['dark image'] = ['',-1.0]
2125                        Data['setDefault'] = False
2126                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2127                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
2128                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2129                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2130                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2131                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2132                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
2133                        self.PickId = Id
2134                        self.Image = Id
2135                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!               
2136                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
2137        finally:
2138            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2139            os.chdir(path)
2140            dlg.Destroy()
2141
2142    def CheckNotebook(self):
2143        '''Make sure the data tree has the minimally expected controls.
2144        (BHT) correct?
2145        '''
2146        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2147            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2148            self.PatternTree.SetItemPyData(sub,[''])
2149        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2150            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2151            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2152        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2153            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2154            self.PatternTree.SetItemPyData(sub,{})
2155        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2156            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2157            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2158        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2159            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2160            self.PatternTree.SetItemPyData(sub,{})
2161        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2162            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2163            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2164                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2165               
2166    class CopyDialog(wx.Dialog):
2167        '''Creates a dialog for copying control settings between
2168        data tree items'''
2169        def __init__(self,parent,title,text,data):
2170            wx.Dialog.__init__(self,parent,-1,title, 
2171                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2172            self.data = data
2173            panel = wx.Panel(self)
2174            mainSizer = wx.BoxSizer(wx.VERTICAL)
2175            topLabl = wx.StaticText(panel,-1,text)
2176            mainSizer.Add((10,10),1)
2177            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2178            mainSizer.Add((10,10),1)
2179            ncols = len(data)/40+1
2180            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2181            for id,item in enumerate(self.data):
2182                ckbox = wx.CheckBox(panel,id,item[1])
2183                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2184                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2185            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2186            OkBtn = wx.Button(panel,-1,"Ok")
2187            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2188            cancelBtn = wx.Button(panel,-1,"Cancel")
2189            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2190            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2191            btnSizer.Add((20,20),1)
2192            btnSizer.Add(OkBtn)
2193            btnSizer.Add((20,20),1)
2194            btnSizer.Add(cancelBtn)
2195            btnSizer.Add((20,20),1)
2196           
2197            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2198            panel.SetSizer(mainSizer)
2199            panel.Fit()
2200            self.Fit()
2201       
2202        def OnCopyChange(self,event):
2203            id = event.GetId()
2204            self.data[id][0] = self.FindWindowById(id).GetValue()       
2205           
2206        def OnOk(self,event):
2207            parent = self.GetParent()
2208            parent.Raise()
2209            self.EndModal(wx.ID_OK)             
2210           
2211        def OnCancel(self,event):
2212            parent = self.GetParent()
2213            parent.Raise()
2214            self.EndModal(wx.ID_CANCEL)             
2215           
2216        def GetData(self):
2217            return self.data
2218       
2219    class SumDialog(wx.Dialog):
2220        'Allows user to supply scale factor(s) when summing data'
2221        def __init__(self,parent,title,text,dataType,data):
2222            wx.Dialog.__init__(self,parent,-1,title, 
2223                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2224            self.data = data
2225            panel = wx.Panel(self)
2226            mainSizer = wx.BoxSizer(wx.VERTICAL)
2227            topLabl = wx.StaticText(panel,-1,text)
2228            mainSizer.Add((10,10),1)
2229            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2230            mainSizer.Add((10,10),1)
2231            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2232            for id,item in enumerate(self.data[:-1]):
2233                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2234                name.SetEditable(False)
2235                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2236                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2237                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2238                dataGridSizer.Add(scale,0,wx.LEFT,10)
2239                dataGridSizer.Add(name,0,wx.RIGHT,10)
2240            if dataType:
2241                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2242                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2243                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2244                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2245                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2246                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2247            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2248            OkBtn = wx.Button(panel,-1,"Ok")
2249            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2250            cancelBtn = wx.Button(panel,-1,"Cancel")
2251            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2252            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2253            btnSizer.Add((20,20),1)
2254            btnSizer.Add(OkBtn)
2255            btnSizer.Add((20,20),1)
2256            btnSizer.Add(cancelBtn)
2257            btnSizer.Add((20,20),1)
2258           
2259            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2260            panel.SetSizer(mainSizer)
2261            panel.Fit()
2262            self.Fit()
2263
2264        def OnScaleChange(self,event):
2265            id = event.GetId()
2266            value = self.FindWindowById(id).GetValue()
2267            try:
2268                self.data[id][0] = float(value)
2269                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2270            except ValueError:
2271                if value and '-' not in value[0]:
2272                    print 'bad input - numbers only'
2273                    self.FindWindowById(id).SetValue('0.000')
2274           
2275        def OnNameChange(self,event):
2276            self.data[-1] = self.name.GetValue() 
2277           
2278        def OnOk(self,event):
2279            parent = self.GetParent()
2280            parent.Raise()
2281            self.EndModal(wx.ID_OK)             
2282           
2283        def OnCancel(self,event):
2284            parent = self.GetParent()
2285            parent.Raise()
2286            self.EndModal(wx.ID_CANCEL)             
2287           
2288        def GetData(self):
2289            return self.data
2290                       
2291    def OnPwdrSum(self,event):
2292        'Sum together powder data(?)'
2293        TextList = []
2294        DataList = []
2295        SumList = []
2296        Names = []
2297        Inst = None
2298        SumItemList = []
2299        Comments = ['Sum equals: \n']
2300        if self.PatternTree.GetCount():
2301            item, cookie = self.PatternTree.GetFirstChild(self.root)
2302            while item:
2303                name = self.PatternTree.GetItemText(item)
2304                Names.append(name)
2305                if 'PWDR' in name:
2306                    TextList.append([0.0,name])
2307                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2308                    if not Inst:
2309                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2310                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2311            if len(TextList) < 2:
2312                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2313                return
2314            TextList.append('default_sum_name')               
2315            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2316            try:
2317                if dlg.ShowModal() == wx.ID_OK:
2318                    lenX = 0
2319                    Xminmax = [0,0]
2320                    Xsum = []
2321                    Ysum = []
2322                    Vsum = []
2323                    result = dlg.GetData()
2324                    for i,item in enumerate(result[:-1]):
2325                        scale,name = item
2326                        data = DataList[i]
2327                        if scale:
2328                            Comments.append("%10.3f %s" % (scale,' * '+name))
2329                            x,y,w,yc,yb,yd = data   #numpy arrays!
2330                            v = 1./w
2331                            if lenX:
2332                                if lenX != len(x):
2333                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2334                                        '\nExpected:'+str(lenX)+ \
2335                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2336                                    return
2337                            else:
2338                                lenX = len(x)
2339                            if Xminmax[1]:
2340                                if Xminmax != [x[0],x[-1]]:
2341                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2342                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2343                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2344                                    return
2345                                else:
2346                                    for j,yi in enumerate(y):
2347                                         Ysum[j] += scale*yi
2348                                         Vsum[j] += abs(scale)*v[j]
2349                            else:
2350                                Xminmax = [x[0],x[-1]]
2351                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2352                                for j,yi in enumerate(y):
2353                                    Xsum.append(x[j])
2354                                    Ysum.append(scale*yi)
2355                                    Vsum.append(abs(scale*v[j]))
2356                    Wsum = 1./np.array(Vsum)
2357                    outname = 'PWDR '+result[-1]
2358                    Id = 0
2359                    if outname in Names:
2360                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2361                        try:
2362                            if dlg2.ShowModal() == wx.ID_OK:
2363                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2364                                self.PatternTree.Delete(Id)
2365                        finally:
2366                            dlg2.Destroy()
2367                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2368                    if Id:
2369                        Sample = G2pdG.SetDefaultSample()
2370                        valuesdict = {
2371                            'wtFactor':1.0,
2372                            'Dummy':False,
2373                            'ranId':ran.randint(0,sys.maxint),
2374                            }
2375                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2376                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2377                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2378                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2379                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2380                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2381                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2382                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2383                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
2384                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
2385                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2386                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2387                        self.PatternTree.SelectItem(Id)
2388                        self.PatternTree.Expand(Id)
2389            finally:
2390                dlg.Destroy()
2391
2392    def OnImageSum(self,event):
2393        'Sum together image data(?)'
2394        TextList = []
2395        DataList = []
2396        SumList = []
2397        Names = []
2398        Inst = []
2399        SumItemList = []
2400        Comments = ['Sum equals: \n']
2401        if self.PatternTree.GetCount():
2402            item, cookie = self.PatternTree.GetFirstChild(self.root)
2403            while item:
2404                name = self.PatternTree.GetItemText(item)
2405                Names.append(name)
2406                if 'IMG' in name:
2407                    TextList.append([0.0,name])
2408                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2409                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2410                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2411            if len(TextList) < 2:
2412                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2413                return
2414            TextList.append('default_sum_name')               
2415            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2416            try:
2417                if dlg.ShowModal() == wx.ID_OK:
2418                    imSize = 0
2419                    result = dlg.GetData()
2420                    First = True
2421                    Found = False
2422                    for i,item in enumerate(result[:-1]):
2423                        scale,name = item
2424                        data = DataList[i]
2425                        if scale:
2426                            Found = True                               
2427                            Comments.append("%10.3f %s" % (scale,' * '+name))
2428                            Npix,imagefile = data
2429                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2430                            if First:
2431                                newImage = np.zeros_like(image)
2432                                First = False
2433                            if imSize:
2434                                if imSize != Npix:
2435                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2436                                        '\nExpected:'+str(imSize)+ \
2437                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2438                                    return
2439                                newImage = newImage+scale*image
2440                            else:
2441                                imSize = Npix
2442                                newImage = newImage+scale*image
2443                            del(image)
2444                    if not Found:
2445                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2446                        return
2447                       
2448                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2449                    outname = 'IMG '+result[-1]
2450                    Id = 0
2451                    if outname in Names:
2452                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2453                        try:
2454                            if dlg2.ShowModal() == wx.ID_OK:
2455                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2456                        finally:
2457                            dlg2.Destroy()
2458                    else:
2459                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2460                    if Id:
2461                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2462                            'G2img files (*.G2img)|*.G2img', 
2463                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2464                        if dlg.ShowModal() == wx.ID_OK:
2465                            newimagefile = dlg.GetPath()
2466                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2467                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2468                            Imax = np.amax(newImage)
2469                            Imin = np.amin(newImage)
2470                            newImage = []
2471                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2472                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2473                        del(newImage)
2474                        if self.imageDefault:
2475                            Data = copy.copy(self.imageDefault)
2476                        Data['showLines'] = True
2477                        Data['ring'] = []
2478                        Data['rings'] = []
2479                        Data['cutoff'] = 10
2480                        Data['pixLimit'] = 20
2481                        Data['ellipses'] = []
2482                        Data['calibrant'] = ''
2483                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2484                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2485                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2486                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2487                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2488                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2489                        self.PatternTree.SelectItem(Id)
2490                        self.PatternTree.Expand(Id)
2491                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2492                        self.Image = self.PickId
2493            finally:
2494                dlg.Destroy()
2495                     
2496    def OnAddPhase(self,event):
2497        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2498        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2499            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2500        else:
2501            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2502        PhaseName = ''
2503        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2504            style=wx.OK)
2505        if dlg.ShowModal() == wx.ID_OK:
2506            PhaseName = dlg.GetValue()
2507        dlg.Destroy()
2508        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2509        E,SGData = G2spc.SpcGroup('P 1')
2510        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2511       
2512    def OnDeletePhase(self,event):
2513        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2514        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2515        if self.dataFrame:
2516            self.dataFrame.Clear() 
2517        TextList = []
2518        DelList = []
2519        DelItemList = []
2520        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2521            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2522        else:
2523            return
2524        if sub:
2525            item, cookie = self.PatternTree.GetFirstChild(sub)
2526            while item:
2527                TextList.append(self.PatternTree.GetItemText(item))
2528                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2529            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2530            try:
2531                if dlg.ShowModal() == wx.ID_OK:
2532                    result = dlg.GetSelections()
2533                    for i in result: DelList.append([i,TextList[i]])
2534                    item, cookie = self.PatternTree.GetFirstChild(sub)
2535                    i = 0
2536                    while item:
2537                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2538                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2539                        i += 1
2540                    for item in DelItemList:
2541                        name = self.PatternTree.GetItemText(item)
2542                        self.PatternTree.Delete(item)
2543                        self.G2plotNB.Delete(name)
2544                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2545                    while item:
2546                        name = self.PatternTree.GetItemText(item)
2547                        if 'PWDR' in name:
2548                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2549                            refList = self.PatternTree.GetItemPyData(Id)
2550                            for i,item in DelList:
2551                                del(refList[item])
2552                            self.PatternTree.SetItemPyData(Id,refList)
2553                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2554            finally:
2555                dlg.Destroy()
2556               
2557    def OnRenameData(self,event):
2558        'Renames an existing phase. Called by Data/Rename Phase menu'
2559        name = self.PatternTree.GetItemText(self.PickId)     
2560        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2561            dataType = name[:name.index(' ')+1]                 #includes the ' '
2562            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2563                defaultValue=name[name.index(' ')+1:])
2564            try:
2565                if dlg.ShowModal() == wx.ID_OK:
2566                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2567            finally:
2568                dlg.Destroy()
2569       
2570    def GetFileList(self,fileType,skip=None):        #potentially useful?
2571        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2572        fileList = []
2573        Source = ''
2574        id, cookie = self.PatternTree.GetFirstChild(self.root)
2575        while id:
2576            name = self.PatternTree.GetItemText(id)
2577            if fileType in name:
2578                if id == skip:
2579                    Source = name
2580                else:
2581                    fileList.append([False,name,id])
2582            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2583        if skip:
2584            return fileList,Source
2585        else:
2586            return fileList
2587           
2588    def OnDataDelete(self, event):
2589        '''Delete one or more histograms from data tree. Called by the
2590        Data/DeleteData menu
2591        '''
2592        TextList = ['All Data']
2593        DelList = []
2594        DelItemList = []
2595        ifPWDR = False
2596        ifSASD = False
2597        ifIMG = False
2598        ifHKLF = False
2599        ifPDF = False
2600        if self.PatternTree.GetCount():
2601            item, cookie = self.PatternTree.GetFirstChild(self.root)
2602            while item:
2603                name = self.PatternTree.GetItemText(item)
2604                if name not in ['Notebook','Controls','Covariance','Constraints',
2605                    'Restraints','Phases','Rigid bodies']:
2606                    if 'PWDR' in name: ifPWDR = True
2607                    if 'SASD' in name: ifSASD = True
2608                    if 'IMG' in name: ifIMG = True
2609                    if 'HKLF' in name: ifHKLF = True
2610                    if 'PDF' in name: ifPDF = True
2611                    TextList.append(name)
2612                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2613            if ifPWDR: TextList.insert(1,'All PWDR')
2614            if ifSASD: TextList.insert(1,'All SASD')
2615            if ifIMG: TextList.insert(1,'All IMG')
2616            if ifHKLF: TextList.insert(1,'All HKLF')
2617            if ifPDF: TextList.insert(1,'All PDF')               
2618            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2619            try:
2620                if dlg.ShowModal() == wx.ID_OK:
2621                    result = dlg.GetSelections()
2622                    for i in result: DelList.append(TextList[i])
2623                    if 'All Data' in DelList:
2624                        DelList = [item for item in TextList if item[:3] != 'All']
2625                    elif 'All PWDR' in DelList:
2626                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2627                    elif 'All SASD' in DelList:
2628                        DelList = [item for item in TextList if item[:4] == 'SASD']
2629                    elif 'All IMG' in DelList:
2630                        DelList = [item for item in TextList if item[:3] == 'IMG']
2631                    elif 'All HKLF' in DelList:
2632                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2633                    elif 'All PDF' in DelList:
2634                        DelList = [item for item in TextList if item[:3] == 'PDF']
2635                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2636                    while item:
2637                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2638                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2639                    for item in DelItemList:
2640                        self.PatternTree.Delete(item)
2641                    self.PickId = 0
2642                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2643            finally:
2644                dlg.Destroy()
2645
2646    def OnFileOpen(self, event, filename=None):
2647        '''Reads in a GSAS-II .gpx project file in response to the
2648        File/Open Project menu button
2649        '''
2650        result = wx.ID_OK
2651        Id = 0
2652        self.EnablePlot = False
2653        if self.PatternTree.GetChildrenCount(self.root,False):
2654            if self.dataFrame:
2655                self.dataFrame.Clear() 
2656            dlg = wx.MessageDialog(
2657                self,
2658                'Do you want to overwrite the current project? '
2659                'Any unsaved changes will be lost. Press OK to continue.',
2660                'Overwrite?',  wx.OK | wx.CANCEL)
2661            try:
2662                result = dlg.ShowModal()
2663                if result == wx.ID_OK:
2664                    self.PatternTree.DeleteChildren(self.root)
2665                    self.GSASprojectfile = ''
2666                    if self.HKL: self.HKL = []
2667                    if self.G2plotNB.plotList:
2668                        self.G2plotNB.clear()
2669            finally:
2670                dlg.Destroy()
2671        if result != wx.ID_OK: return
2672
2673        if not filename:
2674            if self.dataDisplay: self.dataDisplay.Destroy()
2675            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2676                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2677            try:
2678                if dlg.ShowModal() != wx.ID_OK: return
2679                self.GSASprojectfile = dlg.GetPath()
2680                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2681                self.dirname = dlg.GetDirectory()
2682            finally:
2683                dlg.Destroy()
2684        else:
2685            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2686            self.dirname = os.path.split(filename)[0]
2687
2688        G2IO.ProjFileOpen(self)
2689        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2690        self.PatternTree.Expand(self.root)
2691        self.HKL = []
2692        item, cookie = self.PatternTree.GetFirstChild(self.root)
2693        while item and not Id:
2694            name = self.PatternTree.GetItemText(item)
2695            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
2696                Id = item
2697            elif name == 'Controls':
2698                data = self.PatternTree.GetItemPyData(item)
2699                if data:
2700                    for item in self.Refine: item.Enable(True)
2701                    self.EnableSeqRefineMenu()
2702            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2703        if Id:
2704            self.EnablePlot = True
2705            self.PatternTree.SelectItem(Id)
2706        self.CheckNotebook()
2707        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2708
2709    def OnFileClose(self, event):
2710        '''Clears the data tree in response to the
2711        File/New Project menu button. User is given option to save
2712        the project.
2713        '''
2714        if self.dataFrame:
2715            self.dataFrame.Clear()
2716            self.dataFrame.SetLabel('GSAS-II data display') 
2717        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2718        try:
2719            result = dlg.ShowModal()
2720            if result == wx.ID_OK:
2721                self.OnFileSaveMenu(event)
2722            if result != wx.ID_CANCEL:
2723                self.GSASprojectfile = ''
2724                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2725                self.PatternTree.DeleteChildren(self.root)
2726                if self.HKL: self.HKL = []
2727                if self.G2plotNB.plotList:
2728                    self.G2plotNB.clear()
2729        finally:
2730            dlg.Destroy()
2731
2732    def OnFileSave(self, event):
2733        '''Save the current project in response to the
2734        File/Save Project menu button
2735        '''
2736       
2737        if self.GSASprojectfile: 
2738            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2739            G2IO.ProjFileSave(self)
2740        else:
2741            self.OnFileSaveas(event)
2742
2743    def OnFileSaveas(self, event):
2744        '''Save the current project in response to the
2745        File/Save as menu button
2746        '''
2747        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2748            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2749        try:
2750            if dlg.ShowModal() == wx.ID_OK:
2751                self.GSASprojectfile = dlg.GetPath()
2752                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2753                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2754                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2755                G2IO.ProjFileSave(self)
2756                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2757        finally:
2758            dlg.Destroy()
2759
2760    def ExitMain(self, event):
2761        '''Called if the main window is closed'''
2762        if self.undofile:
2763            os.remove(self.undofile)
2764        sys.exit()
2765       
2766    def OnFileExit(self, event):
2767        '''Called in response to the File/Quit menu button'''
2768        if self.dataFrame:
2769            self.dataFrame.Clear() 
2770            self.dataFrame.Destroy()
2771        self.Close()
2772       
2773    # def OnExportPatterns(self,event):
2774    #     names = ['All']
2775    #     exports = []
2776    #     item, cookie = self.PatternTree.GetFirstChild(self.root)
2777    #     while item:
2778    #         name = self.PatternTree.GetItemText(item)
2779    #         if 'PWDR' in name:
2780    #             names.append(name)
2781    #         item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2782    #     if names:
2783    #         dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2784    #         if dlg.ShowModal() == wx.ID_OK:
2785    #             sel = dlg.GetSelections()
2786    #             if sel[0] == 0:
2787    #                 exports = names[1:]
2788    #             else:
2789    #                 for x in sel:
2790    #                     exports.append(names[x])
2791    #         dlg.Destroy()
2792    #     if exports:
2793    #         dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '',
2794    #             'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2795    #             wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2796    #         try:
2797    #             if dlg.ShowModal() == wx.ID_OK:
2798    #                 powderfile = dlg.GetPath()
2799    #                 powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2800    #                 if 'fxye' in powderfile:
2801    #                     G2IO.powderFxyeSave(self,exports,powderfile)
2802    #                 else:       #just xye
2803    #                     G2IO.powderXyeSave(self,exports,powderfile)
2804    #         finally:
2805    #             dlg.Destroy()
2806       
2807    def OnExportPeakList(self,event):
2808        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2809            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2810        try:
2811            if dlg.ShowModal() == wx.ID_OK:
2812                self.peaklistfile = dlg.GetPath()
2813                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2814                file = open(self.peaklistfile,'w')               
2815                item, cookie = self.PatternTree.GetFirstChild(self.root)
2816                while item:
2817                    name = self.PatternTree.GetItemText(item)
2818                    if 'PWDR' in name:
2819                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2820                        while item2:
2821                            name2 = self.PatternTree.GetItemText(item2)
2822                            if name2 == 'Peak List':
2823                                peaks = self.PatternTree.GetItemPyData(item2)
2824                                file.write("%s \n" % (name+' Peak List'))               
2825                                for peak in peaks:
2826                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2827                                        (peak[0],peak[2],peak[4],peak[6]))
2828                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2829                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2830                file.close()
2831        finally:
2832            dlg.Destroy()
2833       
2834    def OnExportHKL(self,event):
2835        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2836            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2837        try:
2838            if dlg.ShowModal() == wx.ID_OK:
2839                self.peaklistfile = dlg.GetPath()
2840                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2841                file = open(self.peaklistfile,'w')               
2842                item, cookie = self.PatternTree.GetFirstChild(self.root)
2843                while item:
2844                    name = self.PatternTree.GetItemText(item)
2845                    if 'PWDR' in name:
2846                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2847                        while item2:
2848                            name2 = self.PatternTree.GetItemText(item2)
2849                            if name2 == 'Reflection Lists':
2850                                data = self.PatternTree.GetItemPyData(item2)
2851                                phases = data.keys()
2852                                for phase in phases:
2853                                    peaks = data[phase]
2854                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2855                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2856                                    for peak in peaks:
2857                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2858                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2859                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2860                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2861                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2862                file.close()
2863        finally:
2864            dlg.Destroy()
2865       
2866    def OnExportPDF(self,event):
2867        #need S(Q) and G(R) to be saved here - probably best from selection?
2868        names = ['All']
2869        exports = []
2870        item, cookie = self.PatternTree.GetFirstChild(self.root)
2871        while item:
2872            name = self.PatternTree.GetItemText(item)
2873            if 'PDF' in name:
2874                names.append(name)
2875            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2876        if names:
2877            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2878            if dlg.ShowModal() == wx.ID_OK:
2879                sel = dlg.GetSelections()
2880                if sel[0] == 0:
2881                    exports = names[1:]
2882                else:
2883                    for x in sel:
2884                        exports.append(names[x])
2885            dlg.Destroy()
2886        if exports:
2887            G2IO.PDFSave(self,exports)
2888       
2889    def OnMakePDFs(self,event):
2890        '''Calculates PDFs
2891        '''
2892        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2893        TextList = ['All PWDR']
2894        PDFlist = []
2895        Names = []
2896        if self.PatternTree.GetCount():
2897            id, cookie = self.PatternTree.GetFirstChild(self.root)
2898            while id:
2899                name = self.PatternTree.GetItemText(id)
2900                Names.append(name)
2901                if 'PWDR' in name:
2902                    TextList.append(name)
2903                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2904            if len(TextList) == 1:
2905                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2906                return
2907            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2908            try:
2909                if dlg.ShowModal() == wx.ID_OK:
2910                    result = dlg.GetSelections()
2911                    for i in result: PDFlist.append(TextList[i])
2912                    if 0 in result:
2913                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2914                    for item in PDFlist:
2915                        PWDRname = item[4:]
2916                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2917                        Data = {
2918                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2919                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2920                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2921                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2922                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2923                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2924                            'Lorch':True,}
2925                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2926                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2927                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2928                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2929                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2930                for item in self.ExportPDF: item.Enable(True)
2931            finally:
2932                dlg.Destroy()
2933               
2934    def GetPWDRdatafromTree(self,PWDRname):
2935        ''' Returns powder data from GSASII tree
2936
2937        :param str PWDRname: a powder histogram name as obtained from
2938          :meth:`GSASIIstruct.GetHistogramNames`
2939
2940        :returns: PWDRdata = powder data dictionary with
2941          Powder data arrays, Limits, Instrument Parameters,
2942          Sample Parameters           
2943        '''
2944        PWDRdata = {}
2945        try:
2946            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2947        except ValueError:
2948            PWDRdata['wtFactor'] = 1.0
2949        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2950        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2951        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2952        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2953        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2954        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2955        if 'ranId' not in PWDRdata:  # patch, add a random Id
2956            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
2957        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
2958            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
2959        return PWDRdata
2960
2961    def GetHKLFdatafromTree(self,HKLFname):
2962        ''' Returns single crystal data from GSASII tree
2963
2964        :param str HKLFname: a single crystal histogram name as obtained
2965          from
2966          :meth:`GSASIIstruct.GetHistogramNames`
2967
2968        :returns: HKLFdata = single crystal data list of reflections
2969
2970        '''
2971        HKLFdata = {}
2972        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2973#        try:
2974#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2975#        except ValueError:
2976#            HKLFdata['wtFactor'] = 1.0
2977        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2978        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2979        return HKLFdata
2980       
2981    def GetPhaseData(self):
2982        '''Returns a dict with defined phases.
2983        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
2984        get same info from GPX file.
2985        '''
2986        phaseData = {}
2987        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2988            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2989        else:
2990            print 'no phases found in GetPhaseData'
2991            sub = None
2992        if sub:
2993            item, cookie = self.PatternTree.GetFirstChild(sub)
2994            while item:
2995                phaseName = self.PatternTree.GetItemText(item)
2996                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2997                if 'ranId' not in phaseData[phaseName]:
2998                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2999                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3000        return phaseData
3001
3002    def GetPhaseInfofromTree(self):
3003        '''Get the phase names and their rId values,
3004        also the histograms used in each phase.
3005
3006        :returns: (phaseRIdList, usedHistograms) where
3007
3008          * phaseRIdList is a list of random Id values for each phase
3009          * usedHistograms is a dict where the keys are the phase names
3010            and the values for each key are a list of the histogram names
3011            used in each phase.
3012        '''
3013        phaseRIdList = []
3014        usedHistograms = {}
3015        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3016        if sub:
3017            item, cookie = self.PatternTree.GetFirstChild(sub)
3018            while item:
3019                phaseName = self.PatternTree.GetItemText(item)
3020                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3021                if ranId: phaseRIdList.append(ranId)
3022                data = self.PatternTree.GetItemPyData(item)
3023                UseList = data['Histograms']
3024                usedHistograms[phaseName] = UseList.keys()
3025                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3026        return phaseRIdList,usedHistograms
3027
3028    def GetPhaseNames(self):
3029        '''Returns a list of defined phases.
3030        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3031        get same info from GPX file.
3032        '''
3033        phaseNames = []
3034        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3035            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3036        else:
3037            print 'no phases found in GetPhaseNames'
3038            sub = None
3039        if sub:
3040            item, cookie = self.PatternTree.GetFirstChild(sub)
3041            while item:
3042                phase = self.PatternTree.GetItemText(item)
3043                phaseNames.append(phase)
3044                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3045        return phaseNames
3046   
3047    def GetHistogramNames(self,hType):
3048        """ Returns a list of histogram names found in the GSASII data tree
3049        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3050        get same info from GPX file.
3051       
3052        :param str hType: list of histogram types
3053        :return: list of histogram names
3054       
3055        """
3056        HistogramNames = []
3057        if self.PatternTree.GetCount():
3058            item, cookie = self.PatternTree.GetFirstChild(self.root)
3059            while item:
3060                name = self.PatternTree.GetItemText(item)
3061                if name[:4] in hType:
3062                    HistogramNames.append(name)       
3063                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3064
3065        return HistogramNames
3066
3067                   
3068    def GetUsedHistogramsAndPhasesfromTree(self):
3069        ''' Returns all histograms that are found in any phase
3070        and any phase that uses a histogram.
3071        This also assigns numbers to used phases and histograms by the
3072        order they appear in the file.
3073        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3074        get same info from GPX file.
3075
3076        :returns: (Histograms,Phases)
3077
3078            * Histograms = dictionary of histograms as {name:data,...}
3079            * Phases = dictionary of phases that use histograms
3080        '''
3081        Histograms = {}
3082        Phases = {}
3083        phaseNames = self.GetPhaseNames()
3084        phaseData = self.GetPhaseData()
3085        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3086
3087        for phase in phaseData:
3088            Phase = phaseData[phase]
3089            pId = phaseNames.index(phase)
3090            Phase['pId'] = pId
3091            if Phase['Histograms']:
3092                if phase not in Phases:
3093                    Phases[phase] = Phase
3094                for hist in Phase['Histograms']:
3095                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3096                        Phase['Histograms'][hist]['Use'] = True         
3097                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3098                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3099                        if item:
3100                            if 'PWDR' in hist[:4]: 
3101                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3102                            elif 'HKLF' in hist[:4]:
3103                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3104                            hId = histoList.index(hist)
3105                            Histograms[hist]['hId'] = hId
3106                        else: # would happen if a referenced histogram were renamed or deleted
3107                            print('For phase "'+str(phase)+
3108                                  '" unresolved reference to histogram "'+str(hist)+'"')
3109        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3110        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3111        return Histograms,Phases
3112       
3113    def MakeLSParmDict(self):
3114        '''Load all parameters used for computation from the tree into a
3115        dict of paired values [value, refine flag]. Note that this is
3116        different than the parmDict used in the refinement, which only has
3117        values.
3118
3119        Note that similar things are done in
3120        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3121        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3122        a GPX file).
3123
3124        :returns: (parmDict,varyList) where:
3125
3126         * parmDict is a dict with values and refinement flags
3127           for each parameter and
3128         * varyList is a list of variables (refined parameters).
3129        '''
3130        parmDict = {}
3131        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3132        for phase in Phases:
3133            if 'pId' not in Phases[phase]:
3134                self.ErrorDialog('View parameter error','You must run least squares at least once')
3135                raise Exception,'No pId for phase '+str(phase)
3136        rigidbodyDict = self.PatternTree.GetItemPyData(   
3137            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3138        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3139        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3140        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3141        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3142        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3143        varyList = rbVary+phaseVary+hapVary+histVary
3144        parmDict.update(rbDict)
3145        parmDict.update(phaseDict)
3146        parmDict.update(hapDict)
3147        parmDict.update(histDict)
3148        for parm in parmDict:
3149            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3150                'Omega','Chi','Phi','nDebye','nPeaks']:
3151                parmDict[parm] = [parmDict[parm],'-']
3152            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3153                parmDict[parm] = [parmDict[parm],'-']
3154            elif parm in varyList:
3155                parmDict[parm] = [parmDict[parm],'T']
3156            else:
3157                parmDict[parm] = [parmDict[parm],'F']
3158        # for i in parmDict: print i,'\t',parmDict[i]
3159        # fl = open('parmDict.dat','wb')
3160        # import cPickle
3161        # cPickle.dump(parmDict,fl,1)
3162        # fl.close()
3163        return parmDict,varyList
3164
3165    def ShowLSParms(self,event):
3166        '''Displays a window showing all parameters in the refinement.
3167        Called from the Calculate/View LS Parms menu.
3168        '''
3169        parmDict,varyList = self.MakeLSParmDict()
3170        parmValDict = {}
3171        for i in parmDict:
3172            parmValDict[i] = parmDict[i][0]
3173           
3174        reqVaryList = tuple(varyList) # save requested variables
3175        try:
3176            # process constraints
3177            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3178            Constraints = self.PatternTree.GetItemPyData(sub)
3179            constList = []
3180            for item in Constraints:
3181                if item.startswith('_'): continue
3182                constList += Constraints[item]
3183            G2mv.InitVars()
3184            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3185            groups,parmlist = G2mv.GroupConstraints(constrDict)
3186            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3187            G2mv.Map2Dict(parmValDict,varyList)
3188        except:
3189            pass
3190        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3191        dlg.ShowModal()
3192        dlg.Destroy()
3193       
3194    def OnRefine(self,event):
3195        '''Perform a refinement.
3196        Called from the Calculate/Refine menu.
3197        '''       
3198        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3199        if Id:
3200            dlg = wx.MessageDialog(
3201                self,
3202                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3203                'Remove sequential results?',wx.OK|wx.CANCEL)
3204            if dlg.ShowModal() == wx.ID_OK:
3205                self.PatternTree.Delete(Id)
3206                dlg.Destroy()
3207            else:
3208                dlg.Destroy()
3209                return
3210
3211        self.OnFileSave(event)
3212        # check that constraints are OK here
3213        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3214        if errmsg:
3215            print('Error in constraints:\n'+errmsg+
3216                  '\nRefinement not possible')
3217            self.ErrorDialog('Constraint Error',
3218                             'Error in constraints:\n'+errmsg+
3219                             '\nRefinement not possible')
3220            return
3221        if warnmsg:
3222            print('Conflict between refinment flag settings and constraints:\n'+
3223                  warnmsg+'\nRefinement not possible')
3224            self.ErrorDialog('Refinement Flag Error',
3225                             'Conflict between refinment flag settings and constraints:\n'+
3226                             warnmsg+
3227                             '\nRefinement not possible')
3228            return
3229        #works - but it'd be better if it could restore plots
3230        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3231            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3232        screenSize = wx.ClientDisplayRect()
3233        Size = dlg.GetSize()
3234        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3235        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3236        dlg.SetSize(Size)
3237        Rw = 100.00
3238        try:
3239            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3240        finally:
3241            dlg.Destroy()
3242        oldId =  self.PatternTree.GetSelection()        #retain current selection
3243        oldName = self.PatternTree.GetItemText(oldId)
3244        parentId = self.PatternTree.GetItemParent(oldId)
3245        parentName = ''
3246        if parentId:
3247            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3248        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3249        try:
3250            if dlg.ShowModal() == wx.ID_OK:
3251                Id = 0
3252                self.PatternTree.DeleteChildren(self.root)
3253                if self.HKL: self.HKL = []
3254                if self.G2plotNB.plotList:
3255                    self.G2plotNB.clear()
3256                G2IO.ProjFileOpen(self)
3257                item, cookie = self.PatternTree.GetFirstChild(self.root)
3258                while item and not Id:
3259                    name = self.PatternTree.GetItemText(item)
3260                    if name[:4] in ['PWDR','HKLF']:
3261                        Id = item
3262                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3263                if Id:
3264                    self.PatternTree.SelectItem(Id)
3265                if parentName:
3266                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3267                    if parentId:
3268                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3269                    else:
3270                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3271                    self.PatternTree.SelectItem(itemId)
3272        finally:
3273            dlg.Destroy()
3274
3275    def OnSeqRefine(self,event):
3276        '''Perform a sequential refinement.
3277        Called from the Calculate/Sequential refine menu.
3278        '''       
3279        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3280        if not Id:
3281            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3282            self.PatternTree.SetItemPyData(Id,{})           
3283        self.OnFileSave(event)
3284        # check that constraints are OK here
3285        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3286        if errmsg:
3287            print('Error in constraints:\n'+errmsg+
3288                  '\nRefinement not possible')
3289            self.ErrorDialog('Constraint Error',
3290                             'Error in constraints:\n'+errmsg+
3291                             '\nRefinement not possible')
3292            return
3293        if warnmsg:
3294            print('Conflict between refinment flag settings and constraints:\n'+
3295                  warnmsg+'\nRefinement not possible')
3296            self.ErrorDialog('Refinement Flag Error',
3297                             'Conflict between refinment flag settings and constraints:\n'+
3298                             warnmsg+'\nRefinement not possible')
3299            return
3300        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3301            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3302        screenSize = wx.ClientDisplayRect()
3303        Size = dlg.GetSize()
3304        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3305        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3306        dlg.SetSize(Size)
3307        try:
3308            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3309        finally:
3310            dlg.Destroy()       
3311        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3312        try:
3313            if dlg.ShowModal() == wx.ID_OK:
3314                Id = 0
3315                self.PatternTree.DeleteChildren(self.root)
3316                if self.HKL: self.HKL = []
3317                if self.G2plotNB.plotList:
3318                    self.G2plotNB.clear()
3319                G2IO.ProjFileOpen(self)
3320                Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3321                self.PatternTree.SelectItem(Id)
3322
3323        finally:
3324            dlg.Destroy()
3325       
3326    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3327        'Display an error message'
3328        result = None
3329        if parent is None:
3330            dlg = wx.MessageDialog(self, message, title,  wtype)
3331        else:
3332            dlg = wx.MessageDialog(parent, message, title,  wtype)
3333            dlg.CenterOnParent() # not working on Mac
3334        try:
3335            result = dlg.ShowModal()
3336        finally:
3337            dlg.Destroy()
3338        return result
3339
3340class GSASIImain(wx.App):
3341    '''Defines a wxApp for GSAS-II
3342
3343    Creates a wx frame (self.main) which contains the display of the
3344    data tree.
3345    '''
3346    def OnInit(self):
3347        '''Called automatically when the app is created.'''
3348        if '2.7' not in sys.version[:5]:
3349            dlg = wx.MessageDialog(None, 
3350                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version[:5],
3351                'Python version error',  wx.OK)
3352            try:
3353                result = dlg.ShowModal()
3354            finally:
3355                dlg.Destroy()
3356            sys.exit()
3357        self.main = GSASII(None)
3358        self.main.Show()
3359        self.SetTopWindow(self.main)
3360        # DEBUG: jump to sequential results
3361        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3362        #self.main.PatternTree.SelectItem(Id)
3363        # end DEBUG
3364        return True
3365    # def MacOpenFile(self, filename):
3366    #     '''Called on Mac every time a file is dropped on the app when it is running,
3367    #     treat this like a File/Open project menu action.
3368    #     Should be ignored on other platforms
3369    #     '''
3370    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3371    #     print 'MacOpen',filename
3372    #     if os.path.splitext(filename)[1] == '.py': return
3373    #     # end PATCH
3374    #     self.main.OnFileOpen(None,filename)
3375    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3376   
3377def main():
3378    '''Start up the GSAS-II application'''
3379    #application = GSASIImain() # don't redirect output, someday we
3380    # may want to do this if we can
3381    application = GSASIImain(0)
3382    if wxInspector:
3383        import wx.lib.inspection as wxeye
3384        wxeye.InspectionTool().Show()
3385
3386    #application.main.OnRefine(None)
3387    application.MainLoop()
3388   
3389if __name__ == '__main__':
3390    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.