source: trunk/GSASII.py @ 1483

Last change on this file since 1483 was 1483, checked in by vondreele, 9 years ago

fix Export of HKLs from PWDR histograms

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