source: trunk/GSASII.py @ 1499

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

Drag/drop of tree items now works; one odd bit if trying to drag to bottom of tree. Sometimes fails, but not catastrophic.

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