source: trunk/GSASII.py @ 1459

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

add instrument parameters (flight path & detector 2-theta) needed for TOF
rework reflection list for TOF
change default diff curve & reflection marker offsets
change weighting to instrument constants calibration to be 1/esd2 from peak fit positions - works a lot better
1st shot at TOF Rietveld refinement with derivatives - need to be checked for correctness (some are wrong)

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