source: trunk/GSASII.py @ 1498

Last change on this file since 1498 was 1498, checked in by vondreele, 7 years ago

try to implement drag/drop of tree items - commented out as it doesn't work
add 'ShowCell?' to Controls to avoid cell errors for sequential single peak fitting when normal sequential refinement has been done
fix a neg. peak width error

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