source: trunk/GSASII.py @ 1506

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

break ReadPowderInstprm? into two pieces; OpenPowderInstprm? is new. This will allow using instprm type lines for default instrument parameters instead of old style GSAS ones.
Implement import of superlattice phase and structure factors from JANA2006 files.
Draw superlattice structure factors in 2D & 3D.
Develop GUI for superlattice phase information; atom displacement, Uij, frac & mag waves.
Remove old style mag stuff.
Fix bug in STD powder import .routine for y=0 points
fix bug in getVCov for missing parameter problem.
IsHistogram? InAnyPhase? now returns histogram name instead of True
Single crystal Reflection list now has menu with plot commands

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