source: trunk/GSASII.py @ 1461

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

modify xye import to not divide by CW (channel width)for TOF data. NB: old style GSAS data is *CW for use in GSAS - need to divide by CW to remove it.
implement spin buttons for lattice parms - get auto update of index peak positions.
fix Refine problem if nothing refined. Now works OK as a profile calculator with no refinement.
More fixes to TOF function & derivatives..

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 159.5 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2014-08-13 20:59:09 +0000 (Wed, 13 Aug 2014) $
6# $Author: vondreele $
7# $Revision: 1461 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1461 2014-08-13 20: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: 1461 $")
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                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
1240                    rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1241                    rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1242                else:       #NB: from topas/fullprof type files
1243                    rd.powderdata[1] = rd.powderdata[1][:-1]
1244                    rd.powderdata[2] = rd.powderdata[2][:-1]
1245                if 'Itype' in Iparm2:
1246                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1247                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1248                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1249                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1250                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1251                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1252                    var += WYI*rd.powderdata[1]**2
1253                    var /= YI**2
1254                    rd.powderdata[2] = 1./var
1255                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1256                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1257                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1258            Tmin = min(rd.powderdata[0])
1259            Tmax = max(rd.powderdata[0])
1260            valuesdict = {
1261                'wtFactor':1.0,
1262                'Dummy':False,
1263                'ranId':ran.randint(0,sys.maxint),
1264                }
1265            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1266            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1267            self.PatternTree.SetItemPyData(
1268                self.PatternTree.AppendItem(Id,text='Comments'),
1269                rd.comments)
1270            self.PatternTree.SetItemPyData(
1271                self.PatternTree.AppendItem(Id,text='Limits'),
1272                [(Tmin,Tmax),[Tmin,Tmax]])
1273            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1274            self.PatternTree.SetItemPyData(
1275                self.PatternTree.AppendItem(Id,text='Background'),
1276                [['chebyschev',True,3,1.0,0.0,0.0],
1277                 {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1278            self.PatternTree.SetItemPyData(
1279                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1280                [Iparm1,Iparm2])
1281            self.PatternTree.SetItemPyData(
1282                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1283                rd.Sample)
1284            self.PatternTree.SetItemPyData(
1285                self.PatternTree.AppendItem(Id,text='Peak List')
1286                ,{'peaks':[],'sigDict':{}})
1287            self.PatternTree.SetItemPyData(
1288                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1289                [[],[]])
1290            self.PatternTree.SetItemPyData(
1291                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1292                [])
1293            self.PatternTree.SetItemPyData(
1294                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1295                {})
1296            newHistList.append(HistName)
1297        else:
1298            self.EnablePlot = True
1299            self.PatternTree.Expand(Id)
1300            self.PatternTree.SelectItem(Id)
1301           
1302        if not newHistList: return # somehow, no new histograms
1303        # make a list of phase names
1304        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1305        phaseNameList = usedHistograms.keys() # phase names in use
1306        if not phaseNameList: return # no phases yet, nothing to do
1307        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1308        for Name in newHistList:
1309            header += '\n  '+str(Name)
1310
1311        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1312        if not result: return
1313        # connect new phases to histograms
1314        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1315        if not sub:
1316            raise Exception('ERROR -- why are there no phases here?')
1317        item, cookie = self.PatternTree.GetFirstChild(sub)
1318        iph = -1
1319        while item: # loop over (new) phases
1320            iph += 1
1321            phaseName = self.PatternTree.GetItemText(item)
1322            data = self.PatternTree.GetItemPyData(item)
1323            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1324            if iph not in result: continue
1325            generalData = data['General']
1326            SGData = generalData['SGData']
1327            UseList = data['Histograms']
1328            NShkl = len(G2spc.MustrainNames(SGData))
1329            NDij = len(G2spc.HStrainNames(SGData))
1330            for histoName in newHistList:
1331                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1332                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1333                refList = self.PatternTree.GetItemPyData(
1334                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1335                refList[generalData['Name']] = []
1336        return # success
1337
1338    def OnDummyPowder(self,event):
1339        '''Called in response to Import/Powder Data/Simulate menu item
1340        to create a Dummy powder diffraction data set.
1341
1342        Reads an instrument parameter file and then gets input from the user
1343        '''
1344        # get a list of existing histograms
1345        PWDRlist = []
1346        if self.PatternTree.GetCount():
1347            item, cookie = self.PatternTree.GetFirstChild(self.root)
1348            while item:
1349                name = self.PatternTree.GetItemText(item)
1350                if name.startswith('PWDR ') and name not in PWDRlist:
1351                    PWDRlist.append(name)
1352                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1353        # Initialize a base class reader
1354        rd = G2IO.ImportPowderData(
1355            extensionlist=tuple(),
1356            strictExtension=False,
1357            formatName = 'Simulate dataset',
1358            longFormatName = 'Compute a simulated pattern')
1359        rd.powderentry[0] = '' # no filename
1360        # #self.powderentry[1] = pos # bank offset (N/A here)
1361        rd.powderentry[2] = 1 # only one bank
1362        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1363        self.CheckNotebook()
1364        Iparm = None
1365        lastIparmfile = ''
1366        lastdatafile = ''
1367        self.zipfile = None
1368        # get instrument parameters for it
1369        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1370        if 'T' in Iparm1['Type'][0]:
1371            print('TOF simulation not supported yet')
1372            return False
1373        else:
1374            # need to get name, 2theta start, end, step
1375            rd.idstring = ' CW'
1376            if 'X' in Iparm1['Type'][0]:
1377                rd.idstring = 'CW x-ray simulation'
1378            else:
1379                rd.idstring = 'CW neutron simulation'
1380            # base initial range on wavelength
1381            wave = Iparm1.get('Lam')
1382            if wave:
1383                wave = wave[0]
1384            else:
1385                wave = Iparm1.get('Lam1')
1386                if wave:
1387                    wave = wave[0]
1388        N = 0
1389        while (N < 3): # insist on a dataset with a few points
1390            names = ('dataset name', 'start angle', 'end angle', 'step size')
1391            if not wave or wave < 1.0:
1392                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1393            else:
1394                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1395            dlg = G2gd.ScrolledMultiEditor(
1396                self,[inp] * len(inp),range(len(inp)),names,
1397                header='Enter simulation name and range',
1398                minvals=(None,0.001,0.001,0.0001),
1399                maxvals=(None,180.,180.,.1),
1400                sizevals=((225,-1),)
1401                )
1402            dlg.CenterOnParent()
1403            if dlg.ShowModal() == wx.ID_OK:
1404                if inp[1] > inp[2]:
1405                    end,start,step = inp[1:]
1406                else:               
1407                    start,end,step = inp[1:]
1408                step = abs(step)
1409            else:
1410                return False
1411            N = int((end-start)/step)+1
1412            x = np.linspace(start,end,N,True)
1413            N = len(x)
1414        rd.powderdata = [
1415            np.array(x), # x-axis values
1416            np.zeros_like(x), # powder pattern intensities
1417            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1418            np.zeros_like(x), # calc. intensities (zero)
1419            np.zeros_like(x), # calc. background (zero)
1420            np.zeros_like(x), # obs-calc profiles
1421            ]
1422        Tmin = rd.powderdata[0][0]
1423        Tmax = rd.powderdata[0][-1]
1424        # data are read, now store them in the tree
1425        HistName = inp[0]
1426        HistName = 'PWDR '+HistName
1427        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1428        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1429        valuesdict = {
1430            'wtFactor':1.0,
1431            'Dummy':True,
1432            'ranId':ran.randint(0,sys.maxint),
1433            }
1434        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1435        self.PatternTree.SetItemPyData(
1436            self.PatternTree.AppendItem(Id,text='Comments'),
1437            rd.comments)
1438        self.PatternTree.SetItemPyData(
1439            self.PatternTree.AppendItem(Id,text='Limits'),
1440            [(Tmin,Tmax),[Tmin,Tmax]])
1441        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1442        self.PatternTree.SetItemPyData(
1443            self.PatternTree.AppendItem(Id,text='Background'),
1444            [['chebyschev',True,3,1.0,0.0,0.0],
1445             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1446        self.PatternTree.SetItemPyData(
1447            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1448            [Iparm1,Iparm2])
1449        self.PatternTree.SetItemPyData(
1450            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1451            rd.Sample)
1452        self.PatternTree.SetItemPyData(
1453            self.PatternTree.AppendItem(Id,text='Peak List')
1454            ,{'peaks':[],'sigDict':{}})
1455        self.PatternTree.SetItemPyData(
1456            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1457            [[],[]])
1458        self.PatternTree.SetItemPyData(
1459            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1460            [])
1461        self.PatternTree.SetItemPyData(
1462            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1463            {})
1464        self.PatternTree.Expand(Id)
1465        self.PatternTree.SelectItem(Id)
1466        print('Added simulation powder data '+str(HistName)+ 
1467              ' with parameters from '+str(rd.instmsg))
1468
1469        # make a list of phase names
1470        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1471        phaseNameList = usedHistograms.keys() # phase names in use
1472        if not phaseNameList: return # no phases yet, nothing to do
1473        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1474        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1475        if not result: return
1476        # connect new phases to histograms
1477        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1478        if not sub:
1479            raise Exception('ERROR -- why are there no phases here?')
1480        item, cookie = self.PatternTree.GetFirstChild(sub)
1481        iph = -1
1482        while item: # loop over (new) phases
1483            iph += 1
1484            phaseName = self.PatternTree.GetItemText(item)
1485            data = self.PatternTree.GetItemPyData(item)
1486            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1487            if iph not in result: continue
1488            generalData = data['General']
1489            SGData = generalData['SGData']
1490            UseList = data['Histograms']
1491            NShkl = len(G2spc.MustrainNames(SGData))
1492            NDij = len(G2spc.HStrainNames(SGData))
1493            UseList[HistName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1494            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1495            refList = self.PatternTree.GetItemPyData(
1496                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1497            refList[generalData['Name']] = []
1498        return # success
1499
1500    def _Add_ImportMenu_smallangle(self,parent):
1501        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1502        '''
1503        submenu = wx.Menu()
1504        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1505            submenu, help='Import small angle data')
1506        for reader in self.ImportSmallAngleReaderlist:
1507            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1508                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1509            self.ImportMenuId[item.GetId()] = reader
1510            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1511        # item = submenu.Append(wx.ID_ANY,
1512        #     help='Import small angle data, use file to try to determine format',
1513        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1514        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1515
1516    def OnImportSmallAngle(self,event):
1517        '''Called in response to an Import/Small Angle Data/... menu item
1518        to read a small angle diffraction data set.
1519        dict self.ImportMenuId is used to look up the specific
1520        reader item associated with the menu item, which will be
1521        None for the last menu item, which is the "guess" option
1522        where all appropriate formats will be tried.
1523
1524        '''
1525       
1526        def GetSASDIparm(reader):
1527            parm = reader.instdict
1528            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1529                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1530            return Iparm,{}
1531           
1532        # get a list of existing histograms
1533        SASDlist = []
1534        if self.PatternTree.GetCount():
1535            item, cookie = self.PatternTree.GetFirstChild(self.root)
1536            while item:
1537                name = self.PatternTree.GetItemText(item)
1538                if name.startswith('SASD ') and name not in SASDlist:
1539                    SASDlist.append(name)
1540                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1541        # look up which format was requested
1542        reqrdr = self.ImportMenuId.get(event.GetId()) 
1543        rdlist = self.OnImportGeneric(
1544            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1545        if len(rdlist) == 0: return
1546        self.CheckNotebook()
1547        Iparm = None
1548        lastdatafile = ''
1549        newHistList = []
1550        self.EnablePlot = False
1551        for rd in rdlist:
1552            lastdatafile = rd.smallangleentry[0]
1553            HistName = rd.idstring
1554            HistName = 'SASD '+HistName
1555            # make new histogram names unique
1556            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1557            print 'Read small angle data '+str(HistName)+ \
1558                ' from file '+str(self.lastimport)
1559            # data are read, now store them in the tree
1560            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1561            Iparm1,Iparm2 = GetSASDIparm(rd)
1562#            if 'T' in Iparm1['Type'][0]:
1563#                if not rd.clockWd and rd.GSAS:
1564#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1565#                cw = np.diff(rd.powderdata[0])
1566#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1567#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1568#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1569#                if 'Itype' in Iparm2:
1570#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1571#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1572#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1573#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1574#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1575#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1576#                    var += WYI*rd.powderdata[1]**2
1577#                    var /= YI**2
1578#                    rd.powderdata[2] = 1./var
1579#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1580#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1581#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1582            Tmin = min(rd.smallangledata[0])
1583            Tmax = max(rd.smallangledata[0])
1584            valuesdict = {
1585                'wtFactor':1.0,
1586                'Dummy':False,
1587                'ranId':ran.randint(0,sys.maxint),
1588                }
1589            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1590            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1591            self.PatternTree.SetItemPyData(
1592                self.PatternTree.AppendItem(Id,text='Comments'),
1593                rd.comments)
1594            self.PatternTree.SetItemPyData(
1595                self.PatternTree.AppendItem(Id,text='Limits'),
1596                [(Tmin,Tmax),[Tmin,Tmax]])
1597            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1598            self.PatternTree.SetItemPyData(
1599                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1600                [Iparm1,Iparm2])
1601            self.PatternTree.SetItemPyData(
1602                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1603            self.PatternTree.SetItemPyData(
1604                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1605                rd.Sample)
1606            self.PatternTree.SetItemPyData(
1607                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1608            newHistList.append(HistName)
1609        else:
1610            self.EnablePlot = True
1611            self.PatternTree.Expand(Id)
1612            self.PatternTree.SelectItem(Id)
1613           
1614        if not newHistList: return # somehow, no new histograms
1615        return # success
1616
1617    def _init_Exports(self,menu):
1618        '''Find exporter routines and add them into menus
1619        '''
1620        # set up the top-level menus
1621        projectmenu = wx.Menu()
1622        item = menu.AppendMenu(
1623            wx.ID_ANY, 'Entire project as',
1624            projectmenu, help='Export entire project')
1625
1626        phasemenu = wx.Menu()
1627        item = menu.AppendMenu(
1628            wx.ID_ANY, 'Phase as',
1629            phasemenu, help='Export phase or sometimes phases')
1630
1631        powdermenu = wx.Menu()
1632        item = menu.AppendMenu(
1633            wx.ID_ANY, 'Powder data as',
1634            powdermenu, help='Export powder diffraction histogram(s)')
1635
1636        singlemenu = wx.Menu()
1637        item = menu.AppendMenu(
1638            wx.ID_ANY, 'Single crystal data as',
1639            singlemenu, help='Export single crystal histogram(s)')
1640
1641        imagemenu = wx.Menu()
1642        item = menu.AppendMenu(
1643            wx.ID_ANY, 'Image data as',
1644            imagemenu, help='Export powder image(s) data')
1645
1646        mapmenu = wx.Menu()
1647        item = menu.AppendMenu(
1648            wx.ID_ANY, 'Maps as',
1649            mapmenu, help='Export density map(s)')
1650
1651        # pdfmenu = wx.Menu()
1652        # item = menu.AppendMenu(
1653        #     wx.ID_ANY, 'PDFs as',
1654        #     pdfmenu, help='Export pair distribution function(s)')
1655
1656        # find all the exporter files
1657        pathlist = sys.path
1658        filelist = []
1659        for path in pathlist:
1660            for filename in glob.iglob(os.path.join(path,"G2export*.py")):
1661                filelist.append(filename)   
1662        filelist = sorted(list(set(filelist))) # remove duplicates
1663        exporterlist = []
1664        # go through the routines and import them, saving objects that
1665        # have export routines (method Exporter)
1666        for filename in filelist:
1667            path,rootname = os.path.split(filename)
1668            pkg = os.path.splitext(rootname)[0]
1669            try:
1670                fp = None
1671                fp, fppath,desc = imp.find_module(pkg,[path,])
1672                pkg = imp.load_module(pkg,fp,fppath,desc)
1673                for clss in inspect.getmembers(pkg): # find classes defined in package
1674                    if clss[0].startswith('_'): continue
1675                    if inspect.isclass(clss[1]):
1676                        # check if we have the required methods
1677                        for m in 'Exporter','loadParmDict':
1678                            if not hasattr(clss[1],m): break
1679                            if not callable(getattr(clss[1],m)): break
1680                        else:
1681                            exporter = clss[1](self) # create an export instance
1682                            exporterlist.append(exporter)
1683            except AttributeError:
1684                print 'Import_'+errprefix+': Attribute Error'+str(filename)
1685                pass
1686            except ImportError:
1687                print 'Import_'+errprefix+': Error importing file'+str(filename)
1688                pass
1689            if fp: fp.close()
1690        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
1691        for obj in exporterlist:
1692            #print 'exporter',obj
1693            for typ in obj.exporttype:
1694                if typ == "project":
1695                    submenu = projectmenu
1696                elif typ == "phase":
1697                    submenu = phasemenu
1698                elif typ == "powder":
1699                    submenu = powdermenu
1700                elif typ == "single":
1701                    submenu = singlemenu
1702                elif typ == "image":
1703                    submenu = imagemenu
1704                elif typ == "map":
1705                    submenu = mapmenu
1706                # elif typ == "pdf":
1707                #     submenu = pdfmenu
1708                else:
1709                    print("Error, unknown type in "+str(obj))
1710                    break
1711                item = submenu.Append(
1712                    wx.ID_ANY,
1713                    help=obj.longFormatName,
1714                    kind=wx.ITEM_NORMAL,
1715                    text=obj.formatName)
1716                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
1717                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
1718               
1719        #code to debug an Exporter. hard-coded the routine below, to allow a reload before use
1720        # def DebugExport(event):
1721        #      print 'start reload'
1722        #      reload(G2IO)
1723        #      import G2export_pwdr as dev
1724        #      reload(dev)
1725        #      dev.ExportPowderFXYE(self).Exporter(event)
1726        # item = menu.Append(
1727        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1728        #     help="debug exporter",text="test Export FXYE")
1729        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
1730        # # #self.ExportLookup[item.GetId()] = 'image'
1731        # self.ExportLookup[item.GetId()] = 'powder'
1732
1733    def _Add_ExportMenuItems(self,parent):
1734        # item = parent.Append(
1735        #     help='Select PWDR item to enable',id=wx.ID_ANY,
1736        #     kind=wx.ITEM_NORMAL,
1737        #     text='Export Powder Patterns...')
1738        # self.ExportPattern.append(item)
1739        # item.Enable(False)
1740        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1741
1742        item = parent.Append(
1743            help='',id=wx.ID_ANY,
1744            kind=wx.ITEM_NORMAL,
1745            text='Export All Peak Lists...')
1746        self.ExportPeakList.append(item)
1747        item.Enable(True)
1748        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1749
1750        item = parent.Append(
1751            help='',id=wx.ID_ANY,
1752            kind=wx.ITEM_NORMAL,
1753            text='Export HKLs...')
1754        self.ExportHKL.append(item)
1755        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1756
1757        item = parent.Append(
1758            help='Select PDF item to enable',
1759            id=wx.ID_ANY,
1760            kind=wx.ITEM_NORMAL,
1761            text='Export PDF...')
1762        self.ExportPDF.append(item)
1763        item.Enable(False)
1764        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1765
1766    def FillMainMenu(self,menubar):
1767        '''Define contents of the main GSAS-II menu for the (main) data tree window
1768        in the mac, used also for the data item windows as well.
1769        '''
1770        File = wx.Menu(title='')
1771        menubar.Append(menu=File, title='&File')
1772        self._Add_FileMenuItems(File)
1773        Data = wx.Menu(title='')
1774        menubar.Append(menu=Data, title='Data')
1775        self._Add_DataMenuItems(Data)
1776        Calculate = wx.Menu(title='')       
1777        menubar.Append(menu=Calculate, title='&Calculate')
1778        self._Add_CalculateMenuItems(Calculate)
1779        Import = wx.Menu(title='')       
1780        menubar.Append(menu=Import, title='Import')
1781        self._Add_ImportMenu_Phase(Import)
1782        self._Add_ImportMenu_powder(Import)
1783        self._Add_ImportMenu_Sfact(Import)
1784        self._Add_ImportMenu_smallangle(Import)
1785        #======================================================================
1786        # Code to help develop/debug an importer, much is hard-coded below
1787        # but module is reloaded before each use, allowing faster testing
1788        # def DebugImport(event):
1789        #     print 'start reload'
1790        #     import G2phase_ISO as dev
1791        #     reload(dev)
1792        #     rd = dev.ISODISTORTPhaseReader()
1793        #     self.ImportMenuId[event.GetId()] = rd
1794        #     self.OnImportPhase(event)
1795            # or ----------------------------------------------------------------------
1796            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
1797            # special debug code
1798            # or ----------------------------------------------------------------------
1799            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
1800            # fp = open(filename,'Ur')
1801            # if not rd.ContentsValidator(fp):
1802            #     print 'not validated'
1803            #     # make a list of used phase ranId's
1804            # phaseRIdList = []
1805            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1806            # if sub:
1807            #     item, cookie = self.PatternTree.GetFirstChild(sub)
1808            #     while item:
1809            #         phaseName = self.PatternTree.GetItemText(item)
1810            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
1811            #         if ranId: phaseRIdList.append(ranId)
1812            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1813            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
1814            #     print 'read OK'
1815        # item = Import.Append(
1816        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1817        #     help="debug importer",text="test importer")
1818        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
1819        #======================================================================
1820        self.ExportMenu = wx.Menu(title='')
1821        menubar.Append(menu=self.ExportMenu, title='Export')
1822        self._init_Exports(self.ExportMenu)
1823        self._Add_ExportMenuItems(self.ExportMenu)
1824        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1825            morehelpitems=[('&Tutorials','Tutorials')])
1826        menubar.Append(menu=HelpMenu,title='&Help')
1827
1828    def _init_ctrls(self, parent):
1829        wx.Frame.__init__(self, name='GSASII', parent=parent,
1830            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1831        clientSize = wx.ClientDisplayRect()
1832        Size = self.GetSize()
1833        xPos = clientSize[2]-Size[0]
1834        self.SetPosition(wx.Point(xPos,clientSize[1]))
1835        self._init_Imports()
1836        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1837        self.MakePDF = []
1838        self.Refine = []
1839        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
1840        #self.ExportPattern = []
1841        self.ExportPeakList = []
1842        self.ExportHKL = []
1843        self.ExportPDF = []
1844        self.ExportPhase = []
1845        self.ExportCIF = []
1846        #
1847        self.GSASIIMenu = wx.MenuBar()
1848        self.FillMainMenu(self.GSASIIMenu)
1849        self.SetMenuBar(self.GSASIIMenu)
1850        self.Bind(wx.EVT_SIZE, self.OnSize)
1851        self.CreateStatusBar()
1852        self.mainPanel = wx.Panel(self,-1)
1853       
1854        wxID_PATTERNTREE = wx.NewId()
1855        self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE,
1856            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1857        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,
1858            self.OnPatternTreeSelChanged, id=wxID_PATTERNTREE)
1859        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1860            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1861        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1862            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1863        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1864            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1865        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1866            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1867        self.root = self.PatternTree.AddRoot('Loaded Data: ')
1868       
1869        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1870            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1871        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1872        plotFrame.Show()
1873       
1874        self.dataDisplay = None
1875       
1876    def __init__(self, parent):
1877        self.ExportLookup = {}
1878        self._init_ctrls(parent)
1879        self.Image = wx.Image(
1880            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
1881            wx.BITMAP_TYPE_ICO)
1882        if "wxMSW" in wx.PlatformInfo:
1883            img = self.Image.Scale(16, 16).ConvertToBitmap()
1884        elif "wxGTK" in wx.PlatformInfo:
1885            img = self.Image.Scale(22, 22).ConvertToBitmap()
1886        else:
1887            img = self.Image.ConvertToBitmap()
1888        self.SetIcon(wx.IconFromBitmap(img))
1889        self.Bind(wx.EVT_CLOSE, self.ExitMain)
1890        # various defaults
1891        self.oldFocus = None
1892        self.GSASprojectfile = ''
1893        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
1894        self.exportDir = None  # the last directory used for exports, if any.
1895        self.undofile = ''
1896        self.TreeItemDelete = False
1897        self.Offset = [0.0,0.0]
1898        self.delOffset = .02
1899        self.refOffset = -1.0
1900        self.refDelt = .01
1901        self.Weight = False
1902        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
1903        self.IfPlot = False
1904        self.PatternId = 0
1905        self.PickId = 0
1906        self.PeakTable = []
1907        self.LimitsTable = []
1908        self.HKL = []
1909        self.Lines = []
1910        self.itemPicked = None
1911        self.dataFrame = None
1912        self.Interpolate = 'nearest'
1913        self.ContourColor = 'Paired'
1914        self.VcovColor = 'RdYlGn'
1915        self.RamaColor = 'Blues'
1916        self.Projection = 'equal area'
1917        self.logPlot = False
1918        self.qPlot = False
1919        self.sqPlot = False
1920        self.SqrtPlot = False
1921        self.ErrorBars = False
1922        self.Contour = False
1923        self.Legend = False
1924        self.SinglePlot = True
1925        self.SubBack = False
1926        self.seqReverse = False
1927        self.plotView = 0
1928        self.Image = 0
1929        self.oldImagefile = ''
1930        self.ImageZ = []
1931        self.Integrate = 0
1932        self.imageDefault = {}
1933        self.Sngl = False
1934        self.ifGetRing = False
1935        self.MaskKey = ''           #trigger for making image masks
1936        self.StrainKey = ''         #ditto for new strain d-zeros
1937        self.EnablePlot = True
1938        self.Tutorials = False      #used for changing default directory
1939        arg = sys.argv
1940        if len(arg) > 1:
1941            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1942            self.dirname = os.path.dirname(arg[1])
1943            if self.dirname: os.chdir(self.dirname)
1944            try:
1945                G2IO.ProjFileOpen(self)
1946                self.PatternTree.Expand(self.root)
1947                for item in self.Refine: item.Enable(True)
1948                self.EnableSeqRefineMenu()
1949
1950            except:
1951                print 'Error opening file',arg[1]
1952
1953    def OnSize(self,event):
1954        'Called when the main window is resized. Not sure why'
1955        w,h = self.GetClientSizeTuple()
1956        self.mainPanel.SetSize(wx.Size(w,h))
1957        self.PatternTree.SetSize(wx.Size(w,h))
1958                       
1959    def OnPatternTreeSelChanged(self, event):
1960        '''Called when a data tree item is selected'''
1961        if self.TreeItemDelete:
1962            self.TreeItemDelete = False
1963        else:
1964            pltNum = self.G2plotNB.nb.GetSelection()
1965            if pltNum >= 0:                         #to avoid the startup with no plot!
1966                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1967                pltPlot = pltPage.figure
1968            item = event.GetItem()
1969            G2gd.MovePatternTreeToGrid(self,item)
1970            if self.oldFocus:
1971                self.oldFocus.SetFocus()
1972       
1973    def OnPatternTreeItemCollapsed(self, event):
1974        'Called when a tree item is collapsed'
1975        event.Skip()
1976
1977    def OnPatternTreeItemExpanded(self, event):
1978        'Called when a tree item is expanded'
1979        event.Skip()
1980       
1981    def OnPatternTreeItemDelete(self, event):
1982        'Called when a tree item is deleted -- not sure what this does'
1983        self.TreeItemDelete = True
1984
1985    def OnPatternTreeItemActivated(self, event):
1986        'Called when a tree item is activated'
1987        event.Skip()
1988       
1989    def OnPatternTreeKeyDown(self,event):
1990        'Not sure what this does'
1991        key = event.GetKeyCode()
1992        item = self.PickId
1993        if type(item) is int: return # is this the toplevel in tree?
1994        if key == wx.WXK_UP:
1995            self.oldFocus = wx.Window.FindFocus()
1996            self.PatternTree.GetPrevSibling(item)
1997        elif key == wx.WXK_DOWN:
1998            self.oldFocus = wx.Window.FindFocus()
1999            self.PatternTree.GetNextSibling(item)
2000               
2001    def OnReadPowderPeaks(self,event):
2002        'Bound to menu Data/Read Powder Peaks -- still needed?'
2003        Cuka = 1.54052
2004        self.CheckNotebook()
2005        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
2006            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
2007        try:
2008            if dlg.ShowModal() == wx.ID_OK:
2009                self.HKL = []
2010                self.powderfile = dlg.GetPath()
2011                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
2012                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2013                data = ['PKS',Cuka,0.0]
2014                names = ['Type','Lam','Zero'] 
2015                codes = [0,0,0]
2016                inst = [G2IO.makeInstDict(names,data,codes),{}]
2017                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2018                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2019                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2020                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2021                self.PatternTree.Expand(Id)
2022                self.PatternTree.SelectItem(Id)
2023                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2024        finally:
2025            dlg.Destroy()
2026                       
2027    def OnImageRead(self,event):
2028        'Called to read in an image in any known format'
2029        self.CheckNotebook()
2030        dlg = wx.FileDialog(
2031            self, 'Choose image files', '.', '',
2032            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png)|'
2033            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png;*.zip|'
2034            'European detector file (*.edf)|*.edf|'
2035            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2036            'MAR file (*.mar*)|*.mar*|'
2037            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2038            'ADSC Image (*.img)|*.img|'
2039            'GSAS-II Image (*.G2img)|*.G2img|'
2040            'Portable Network Graphics image (*.png)|*.png|'
2041            'Zip archive (*.zip)|*.zip|'
2042            'All files (*.*)|*.*',
2043            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
2044        try:
2045            if dlg.ShowModal() == wx.ID_OK:
2046                imagefiles = dlg.GetPaths()
2047                imagefiles.sort()
2048                for imagefile in imagefiles:
2049                    # if a zip file, open and extract
2050                    if os.path.splitext(imagefile)[1].lower() == '.zip':
2051                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
2052                        if extractedfile is not None and extractedfile != imagefile:
2053                            imagefile = extractedfile
2054                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
2055                    if Comments:
2056                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
2057                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2058                        Imax = np.amax(Image)
2059                        Imin = max(0.0,np.amin(Image))          #force positive
2060                        if self.imageDefault:
2061                            Data = copy.copy(self.imageDefault)
2062                            Data['showLines'] = True
2063                            Data['ring'] = []
2064                            Data['rings'] = []
2065                            Data['cutoff'] = 10
2066                            Data['pixLimit'] = 20
2067                            Data['edgemin'] = 100000000
2068                            Data['calibdmin'] = 0.5
2069                            Data['calibskip'] = 0
2070                            Data['ellipses'] = []
2071                            Data['calibrant'] = ''
2072                            Data['GonioAngles'] = [0.,0.,0.]
2073                            Data['DetDepthRef'] = False
2074                        else:
2075                            Data['type'] = 'PWDR'
2076                            Data['color'] = 'Paired'
2077                            Data['tilt'] = 0.0
2078                            Data['rotation'] = 0.0
2079                            Data['showLines'] = False
2080                            Data['ring'] = []
2081                            Data['rings'] = []
2082                            Data['cutoff'] = 10
2083                            Data['pixLimit'] = 20
2084                            Data['calibdmin'] = 0.5
2085                            Data['calibskip'] = 0
2086                            Data['edgemin'] = 100000000
2087                            Data['ellipses'] = []
2088                            Data['GonioAngles'] = [0.,0.,0.]
2089                            Data['DetDepth'] = 0.
2090                            Data['DetDepthRef'] = False
2091                            Data['calibrant'] = ''
2092                            Data['IOtth'] = [2.0,5.0]
2093                            Data['LRazimuth'] = [135,225]
2094                            Data['azmthOff'] = 0.0
2095                            Data['outChannels'] = 2500
2096                            Data['outAzimuths'] = 1
2097                            Data['centerAzm'] = False
2098                            Data['fullIntegrate'] = False
2099                            Data['setRings'] = False
2100                            Data['background image'] = ['',-1.0]                           
2101                            Data['dark image'] = ['',-1.0]
2102                        Data['setDefault'] = False
2103                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2104                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
2105                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2106                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2107                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2108                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2109                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
2110                        self.PickId = Id
2111                        self.Image = Id
2112                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!               
2113                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
2114        finally:
2115            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2116            os.chdir(path)
2117            dlg.Destroy()
2118
2119    def CheckNotebook(self):
2120        '''Make sure the data tree has the minimally expected controls.
2121        (BHT) correct?
2122        '''
2123        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2124            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2125            self.PatternTree.SetItemPyData(sub,[''])
2126        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2127            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2128            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2129        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2130            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2131            self.PatternTree.SetItemPyData(sub,{})
2132        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2133            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2134            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2135        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2136            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2137            self.PatternTree.SetItemPyData(sub,{})
2138        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2139            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2140            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2141                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2142               
2143    class CopyDialog(wx.Dialog):
2144        '''Creates a dialog for copying control settings between
2145        data tree items'''
2146        def __init__(self,parent,title,text,data):
2147            wx.Dialog.__init__(self,parent,-1,title, 
2148                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2149            self.data = data
2150            panel = wx.Panel(self)
2151            mainSizer = wx.BoxSizer(wx.VERTICAL)
2152            topLabl = wx.StaticText(panel,-1,text)
2153            mainSizer.Add((10,10),1)
2154            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2155            mainSizer.Add((10,10),1)
2156            ncols = len(data)/40+1
2157            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2158            for id,item in enumerate(self.data):
2159                ckbox = wx.CheckBox(panel,id,item[1])
2160                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2161                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2162            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2163            OkBtn = wx.Button(panel,-1,"Ok")
2164            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2165            cancelBtn = wx.Button(panel,-1,"Cancel")
2166            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2167            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2168            btnSizer.Add((20,20),1)
2169            btnSizer.Add(OkBtn)
2170            btnSizer.Add((20,20),1)
2171            btnSizer.Add(cancelBtn)
2172            btnSizer.Add((20,20),1)
2173           
2174            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2175            panel.SetSizer(mainSizer)
2176            panel.Fit()
2177            self.Fit()
2178       
2179        def OnCopyChange(self,event):
2180            id = event.GetId()
2181            self.data[id][0] = self.FindWindowById(id).GetValue()       
2182           
2183        def OnOk(self,event):
2184            parent = self.GetParent()
2185            parent.Raise()
2186            self.EndModal(wx.ID_OK)             
2187           
2188        def OnCancel(self,event):
2189            parent = self.GetParent()
2190            parent.Raise()
2191            self.EndModal(wx.ID_CANCEL)             
2192           
2193        def GetData(self):
2194            return self.data
2195       
2196    class SumDialog(wx.Dialog):
2197        'Allows user to supply scale factor(s) when summing data'
2198        def __init__(self,parent,title,text,dataType,data):
2199            wx.Dialog.__init__(self,parent,-1,title, 
2200                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2201            self.data = data
2202            panel = wx.Panel(self)
2203            mainSizer = wx.BoxSizer(wx.VERTICAL)
2204            topLabl = wx.StaticText(panel,-1,text)
2205            mainSizer.Add((10,10),1)
2206            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2207            mainSizer.Add((10,10),1)
2208            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2209            for id,item in enumerate(self.data[:-1]):
2210                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2211                name.SetEditable(False)
2212                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2213                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2214                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2215                dataGridSizer.Add(scale,0,wx.LEFT,10)
2216                dataGridSizer.Add(name,0,wx.RIGHT,10)
2217            if dataType:
2218                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2219                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2220                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2221                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2222                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2223                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2224            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2225            OkBtn = wx.Button(panel,-1,"Ok")
2226            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2227            cancelBtn = wx.Button(panel,-1,"Cancel")
2228            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2229            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2230            btnSizer.Add((20,20),1)
2231            btnSizer.Add(OkBtn)
2232            btnSizer.Add((20,20),1)
2233            btnSizer.Add(cancelBtn)
2234            btnSizer.Add((20,20),1)
2235           
2236            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2237            panel.SetSizer(mainSizer)
2238            panel.Fit()
2239            self.Fit()
2240
2241        def OnScaleChange(self,event):
2242            id = event.GetId()
2243            value = self.FindWindowById(id).GetValue()
2244            try:
2245                self.data[id][0] = float(value)
2246                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2247            except ValueError:
2248                if value and '-' not in value[0]:
2249                    print 'bad input - numbers only'
2250                    self.FindWindowById(id).SetValue('0.000')
2251           
2252        def OnNameChange(self,event):
2253            self.data[-1] = self.name.GetValue() 
2254           
2255        def OnOk(self,event):
2256            parent = self.GetParent()
2257            parent.Raise()
2258            self.EndModal(wx.ID_OK)             
2259           
2260        def OnCancel(self,event):
2261            parent = self.GetParent()
2262            parent.Raise()
2263            self.EndModal(wx.ID_CANCEL)             
2264           
2265        def GetData(self):
2266            return self.data
2267                       
2268    def OnPwdrSum(self,event):
2269        'Sum together powder data(?)'
2270        TextList = []
2271        DataList = []
2272        SumList = []
2273        Names = []
2274        Inst = None
2275        SumItemList = []
2276        Comments = ['Sum equals: \n']
2277        if self.PatternTree.GetCount():
2278            item, cookie = self.PatternTree.GetFirstChild(self.root)
2279            while item:
2280                name = self.PatternTree.GetItemText(item)
2281                Names.append(name)
2282                if 'PWDR' in name:
2283                    TextList.append([0.0,name])
2284                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2285                    if not Inst:
2286                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2287                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2288            if len(TextList) < 2:
2289                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2290                return
2291            TextList.append('default_sum_name')               
2292            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2293            try:
2294                if dlg.ShowModal() == wx.ID_OK:
2295                    lenX = 0
2296                    Xminmax = [0,0]
2297                    Xsum = []
2298                    Ysum = []
2299                    Vsum = []
2300                    result = dlg.GetData()
2301                    for i,item in enumerate(result[:-1]):
2302                        scale,name = item
2303                        data = DataList[i]
2304                        if scale:
2305                            Comments.append("%10.3f %s" % (scale,' * '+name))
2306                            x,y,w,yc,yb,yd = data   #numpy arrays!
2307                            v = 1./w
2308                            if lenX:
2309                                if lenX != len(x):
2310                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2311                                        '\nExpected:'+str(lenX)+ \
2312                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2313                                    return
2314                            else:
2315                                lenX = len(x)
2316                            if Xminmax[1]:
2317                                if Xminmax != [x[0],x[-1]]:
2318                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2319                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2320                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2321                                    return
2322                                else:
2323                                    for j,yi in enumerate(y):
2324                                         Ysum[j] += scale*yi
2325                                         Vsum[j] += abs(scale)*v[j]
2326                            else:
2327                                Xminmax = [x[0],x[-1]]
2328                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2329                                for j,yi in enumerate(y):
2330                                    Xsum.append(x[j])
2331                                    Ysum.append(scale*yi)
2332                                    Vsum.append(abs(scale*v[j]))
2333                    Wsum = 1./np.array(Vsum)
2334                    outname = 'PWDR '+result[-1]
2335                    Id = 0
2336                    if outname in Names:
2337                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2338                        try:
2339                            if dlg2.ShowModal() == wx.ID_OK:
2340                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2341                                self.PatternTree.Delete(Id)
2342                        finally:
2343                            dlg2.Destroy()
2344                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2345                    if Id:
2346                        Sample = G2pdG.SetDefaultSample()
2347                        valuesdict = {
2348                            'wtFactor':1.0,
2349                            'Dummy':False,
2350                            'ranId':ran.randint(0,sys.maxint),
2351                            }
2352                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2353                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2354                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2355                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2356                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2357                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2358                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2359                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2360                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2361                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2362                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2363                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2364                        self.PatternTree.SelectItem(Id)
2365                        self.PatternTree.Expand(Id)
2366            finally:
2367                dlg.Destroy()
2368
2369    def OnImageSum(self,event):
2370        'Sum together image data(?)'
2371        TextList = []
2372        DataList = []
2373        SumList = []
2374        Names = []
2375        Inst = []
2376        SumItemList = []
2377        Comments = ['Sum equals: \n']
2378        if self.PatternTree.GetCount():
2379            item, cookie = self.PatternTree.GetFirstChild(self.root)
2380            while item:
2381                name = self.PatternTree.GetItemText(item)
2382                Names.append(name)
2383                if 'IMG' in name:
2384                    TextList.append([0.0,name])
2385                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2386                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2387                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2388            if len(TextList) < 2:
2389                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2390                return
2391            TextList.append('default_sum_name')               
2392            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2393            try:
2394                if dlg.ShowModal() == wx.ID_OK:
2395                    imSize = 0
2396                    result = dlg.GetData()
2397                    First = True
2398                    Found = False
2399                    for i,item in enumerate(result[:-1]):
2400                        scale,name = item
2401                        data = DataList[i]
2402                        if scale:
2403                            Found = True                               
2404                            Comments.append("%10.3f %s" % (scale,' * '+name))
2405                            Npix,imagefile = data
2406                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2407                            if First:
2408                                newImage = np.zeros_like(image)
2409                                First = False
2410                            if imSize:
2411                                if imSize != Npix:
2412                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2413                                        '\nExpected:'+str(imSize)+ \
2414                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2415                                    return
2416                                newImage = newImage+scale*image
2417                            else:
2418                                imSize = Npix
2419                                newImage = newImage+scale*image
2420                            del(image)
2421                    if not Found:
2422                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2423                        return
2424                       
2425                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2426                    outname = 'IMG '+result[-1]
2427                    Id = 0
2428                    if outname in Names:
2429                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2430                        try:
2431                            if dlg2.ShowModal() == wx.ID_OK:
2432                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2433                        finally:
2434                            dlg2.Destroy()
2435                    else:
2436                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2437                    if Id:
2438                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2439                            'G2img files (*.G2img)|*.G2img', 
2440                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2441                        if dlg.ShowModal() == wx.ID_OK:
2442                            newimagefile = dlg.GetPath()
2443                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2444                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2445                            Imax = np.amax(newImage)
2446                            Imin = np.amin(newImage)
2447                            newImage = []
2448                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2449                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2450                        del(newImage)
2451                        if self.imageDefault:
2452                            Data = copy.copy(self.imageDefault)
2453                        Data['showLines'] = True
2454                        Data['ring'] = []
2455                        Data['rings'] = []
2456                        Data['cutoff'] = 10
2457                        Data['pixLimit'] = 20
2458                        Data['ellipses'] = []
2459                        Data['calibrant'] = ''
2460                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2461                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2462                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2463                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2464                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2465                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2466                        self.PatternTree.SelectItem(Id)
2467                        self.PatternTree.Expand(Id)
2468                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2469                        self.Image = self.PickId
2470            finally:
2471                dlg.Destroy()
2472                     
2473    def OnAddPhase(self,event):
2474        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2475        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2476            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2477        else:
2478            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2479        PhaseName = ''
2480        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2481            style=wx.OK)
2482        if dlg.ShowModal() == wx.ID_OK:
2483            PhaseName = dlg.GetValue()
2484        dlg.Destroy()
2485        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2486        E,SGData = G2spc.SpcGroup('P 1')
2487        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2488       
2489    def OnDeletePhase(self,event):
2490        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2491        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2492        if self.dataFrame:
2493            self.dataFrame.Clear() 
2494        TextList = []
2495        DelList = []
2496        DelItemList = []
2497        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2498            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2499        else:
2500            return
2501        if sub:
2502            item, cookie = self.PatternTree.GetFirstChild(sub)
2503            while item:
2504                TextList.append(self.PatternTree.GetItemText(item))
2505                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2506            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2507            try:
2508                if dlg.ShowModal() == wx.ID_OK:
2509                    result = dlg.GetSelections()
2510                    for i in result: DelList.append([i,TextList[i]])
2511                    item, cookie = self.PatternTree.GetFirstChild(sub)
2512                    i = 0
2513                    while item:
2514                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2515                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2516                        i += 1
2517                    for item in DelItemList:
2518                        name = self.PatternTree.GetItemText(item)
2519                        self.PatternTree.Delete(item)
2520                        self.G2plotNB.Delete(name)
2521                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2522                    while item:
2523                        name = self.PatternTree.GetItemText(item)
2524                        if 'PWDR' in name:
2525                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2526                            refList = self.PatternTree.GetItemPyData(Id)
2527                            for i,item in DelList:
2528                                del(refList[item])
2529                            self.PatternTree.SetItemPyData(Id,refList)
2530                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2531            finally:
2532                dlg.Destroy()
2533               
2534    def OnRenameData(self,event):
2535        'Renames an existing phase. Called by Data/Rename Phase menu'
2536        name = self.PatternTree.GetItemText(self.PickId)     
2537        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2538            dataType = name[:name.index(' ')+1]                 #includes the ' '
2539            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2540                defaultValue=name[name.index(' ')+1:])
2541            try:
2542                if dlg.ShowModal() == wx.ID_OK:
2543                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2544            finally:
2545                dlg.Destroy()
2546       
2547    def GetFileList(self,fileType,skip=None):        #potentially useful?
2548        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2549        fileList = []
2550        Source = ''
2551        id, cookie = self.PatternTree.GetFirstChild(self.root)
2552        while id:
2553            name = self.PatternTree.GetItemText(id)
2554            if fileType in name:
2555                if id == skip:
2556                    Source = name
2557                else:
2558                    fileList.append([False,name,id])
2559            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2560        if skip:
2561            return fileList,Source
2562        else:
2563            return fileList
2564           
2565    def OnDataDelete(self, event):
2566        '''Delete one or more histograms from data tree. Called by the
2567        Data/DeleteData menu
2568        '''
2569        TextList = ['All Data']
2570        DelList = []
2571        DelItemList = []
2572        ifPWDR = False
2573        ifSASD = False
2574        ifIMG = False
2575        ifHKLF = False
2576        ifPDF = False
2577        if self.PatternTree.GetCount():
2578            item, cookie = self.PatternTree.GetFirstChild(self.root)
2579            while item:
2580                name = self.PatternTree.GetItemText(item)
2581                if name not in ['Notebook','Controls','Covariance','Constraints',
2582                    'Restraints','Phases','Rigid bodies']:
2583                    if 'PWDR' in name: ifPWDR = True
2584                    if 'SASD' in name: ifSASD = True
2585                    if 'IMG' in name: ifIMG = True
2586                    if 'HKLF' in name: ifHKLF = True
2587                    if 'PDF' in name: ifPDF = True
2588                    TextList.append(name)
2589                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2590            if ifPWDR: TextList.insert(1,'All PWDR')
2591            if ifSASD: TextList.insert(1,'All SASD')
2592            if ifIMG: TextList.insert(1,'All IMG')
2593            if ifHKLF: TextList.insert(1,'All HKLF')
2594            if ifPDF: TextList.insert(1,'All PDF')               
2595            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2596            try:
2597                if dlg.ShowModal() == wx.ID_OK:
2598                    result = dlg.GetSelections()
2599                    for i in result: DelList.append(TextList[i])
2600                    if 'All Data' in DelList:
2601                        DelList = [item for item in TextList if item[:3] != 'All']
2602                    elif 'All PWDR' in DelList:
2603                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2604                    elif 'All SASD' in DelList:
2605                        DelList = [item for item in TextList if item[:4] == 'SASD']
2606                    elif 'All IMG' in DelList:
2607                        DelList = [item for item in TextList if item[:3] == 'IMG']
2608                    elif 'All HKLF' in DelList:
2609                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2610                    elif 'All PDF' in DelList:
2611                        DelList = [item for item in TextList if item[:3] == 'PDF']
2612                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2613                    while item:
2614                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2615                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2616                    for item in DelItemList:
2617                        self.PatternTree.Delete(item)
2618                    self.PickId = 0
2619                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2620            finally:
2621                dlg.Destroy()
2622
2623    def OnFileOpen(self, event, filename=None):
2624        '''Reads in a GSAS-II .gpx project file in response to the
2625        File/Open Project menu button
2626        '''
2627        result = wx.ID_OK
2628        Id = 0
2629        self.EnablePlot = False
2630        if self.PatternTree.GetChildrenCount(self.root,False):
2631            if self.dataFrame:
2632                self.dataFrame.Clear() 
2633            dlg = wx.MessageDialog(
2634                self,
2635                'Do you want to overwrite the current project? '
2636                'Any unsaved changes will be lost. Press OK to continue.',
2637                'Overwrite?',  wx.OK | wx.CANCEL)
2638            try:
2639                result = dlg.ShowModal()
2640                if result == wx.ID_OK:
2641                    self.PatternTree.DeleteChildren(self.root)
2642                    self.GSASprojectfile = ''
2643                    if self.HKL: self.HKL = []
2644                    if self.G2plotNB.plotList:
2645                        self.G2plotNB.clear()
2646            finally:
2647                dlg.Destroy()
2648        if result != wx.ID_OK: return
2649
2650        if not filename:
2651            if self.dataDisplay: self.dataDisplay.Destroy()
2652            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2653                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2654            try:
2655                if dlg.ShowModal() != wx.ID_OK: return
2656                self.GSASprojectfile = dlg.GetPath()
2657                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2658                self.dirname = dlg.GetDirectory()
2659            finally:
2660                dlg.Destroy()
2661        else:
2662            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2663            self.dirname = os.path.split(filename)[0]
2664
2665        G2IO.ProjFileOpen(self)
2666        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2667        self.PatternTree.Expand(self.root)
2668        self.HKL = []
2669        item, cookie = self.PatternTree.GetFirstChild(self.root)
2670        while item and not Id:
2671            name = self.PatternTree.GetItemText(item)
2672            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
2673                Id = item
2674            elif name == 'Controls':
2675                data = self.PatternTree.GetItemPyData(item)
2676                if data:
2677                    for item in self.Refine: item.Enable(True)
2678                    self.EnableSeqRefineMenu()
2679            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2680        if Id:
2681            self.EnablePlot = True
2682            self.PatternTree.SelectItem(Id)
2683        self.CheckNotebook()
2684        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2685
2686    def OnFileClose(self, event):
2687        '''Clears the data tree in response to the
2688        File/New Project menu button. User is given option to save
2689        the project.
2690        '''
2691        if self.dataFrame:
2692            self.dataFrame.Clear()
2693            self.dataFrame.SetLabel('GSAS-II data display') 
2694        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2695        try:
2696            result = dlg.ShowModal()
2697            if result == wx.ID_OK:
2698                self.OnFileSaveMenu(event)
2699            if result != wx.ID_CANCEL:
2700                self.GSASprojectfile = ''
2701                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2702                self.PatternTree.DeleteChildren(self.root)
2703                if self.HKL: self.HKL = []
2704                if self.G2plotNB.plotList:
2705                    self.G2plotNB.clear()
2706        finally:
2707            dlg.Destroy()
2708
2709    def OnFileSave(self, event):
2710        '''Save the current project in response to the
2711        File/Save Project menu button
2712        '''
2713       
2714        if self.GSASprojectfile: 
2715            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2716            G2IO.ProjFileSave(self)
2717        else:
2718            self.OnFileSaveas(event)
2719
2720    def OnFileSaveas(self, event):
2721        '''Save the current project in response to the
2722        File/Save as menu button
2723        '''
2724        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2725            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2726        try:
2727            if dlg.ShowModal() == wx.ID_OK:
2728                self.GSASprojectfile = dlg.GetPath()
2729                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2730                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2731                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2732                G2IO.ProjFileSave(self)
2733                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2734        finally:
2735            dlg.Destroy()
2736
2737    def ExitMain(self, event):
2738        '''Called if the main window is closed'''
2739        if self.undofile:
2740            os.remove(self.undofile)
2741        sys.exit()
2742       
2743    def OnFileExit(self, event):
2744        '''Called in response to the File/Quit menu button'''
2745        if self.dataFrame:
2746            self.dataFrame.Clear() 
2747            self.dataFrame.Destroy()
2748        self.Close()
2749       
2750    # def OnExportPatterns(self,event):
2751    #     names = ['All']
2752    #     exports = []
2753    #     item, cookie = self.PatternTree.GetFirstChild(self.root)
2754    #     while item:
2755    #         name = self.PatternTree.GetItemText(item)
2756    #         if 'PWDR' in name:
2757    #             names.append(name)
2758    #         item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2759    #     if names:
2760    #         dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2761    #         if dlg.ShowModal() == wx.ID_OK:
2762    #             sel = dlg.GetSelections()
2763    #             if sel[0] == 0:
2764    #                 exports = names[1:]
2765    #             else:
2766    #                 for x in sel:
2767    #                     exports.append(names[x])
2768    #         dlg.Destroy()
2769    #     if exports:
2770    #         dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '',
2771    #             'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2772    #             wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2773    #         try:
2774    #             if dlg.ShowModal() == wx.ID_OK:
2775    #                 powderfile = dlg.GetPath()
2776    #                 powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2777    #                 if 'fxye' in powderfile:
2778    #                     G2IO.powderFxyeSave(self,exports,powderfile)
2779    #                 else:       #just xye
2780    #                     G2IO.powderXyeSave(self,exports,powderfile)
2781    #         finally:
2782    #             dlg.Destroy()
2783       
2784    def OnExportPeakList(self,event):
2785        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2786            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2787        try:
2788            if dlg.ShowModal() == wx.ID_OK:
2789                self.peaklistfile = dlg.GetPath()
2790                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2791                file = open(self.peaklistfile,'w')               
2792                item, cookie = self.PatternTree.GetFirstChild(self.root)
2793                while item:
2794                    name = self.PatternTree.GetItemText(item)
2795                    if 'PWDR' in name:
2796                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2797                        while item2:
2798                            name2 = self.PatternTree.GetItemText(item2)
2799                            if name2 == 'Peak List':
2800                                peaks = self.PatternTree.GetItemPyData(item2)['peaks']
2801                                file.write("%s \n" % (name+' Peak List'))
2802                                if len(peaks[0]) == 8:
2803                                    file.write('%10s %12s %10s %10s\n'%('pos','int','sig','gam'))
2804                                else:
2805                                    file.write('%10s %12s %10s %10s %10s %10s\n'%('pos','int','alp','bet','sig','gam'))                                   
2806                                for peak in peaks:
2807                                    if len(peak) == 8:  #CW
2808                                        file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2809                                            (peak[0],peak[2],peak[4],peak[6]))
2810                                    else:               #TOF - more cols
2811                                        file.write("%10.5f %12.2f %10.3f %10.3f %10.3f %10.3f\n" % \
2812                                            (peak[0],peak[2],peak[4],peak[6],peak[8],peak[10]))
2813                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2814                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2815                file.close()
2816        finally:
2817            dlg.Destroy()
2818       
2819    def OnExportHKL(self,event):
2820        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2821            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2822        try:
2823            if dlg.ShowModal() == wx.ID_OK:
2824                self.peaklistfile = dlg.GetPath()
2825                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2826                file = open(self.peaklistfile,'w')               
2827                item, cookie = self.PatternTree.GetFirstChild(self.root)
2828                while item:
2829                    name = self.PatternTree.GetItemText(item)
2830                    if 'PWDR' in name:
2831                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2832                        while item2:
2833                            name2 = self.PatternTree.GetItemText(item2)
2834                            if name2 == 'Reflection Lists':
2835                                data = self.PatternTree.GetItemPyData(item2)
2836                                phases = data.keys()
2837                                for phase in phases:
2838                                    peaks = data[phase]
2839                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2840                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2841                                    for peak in peaks:
2842                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2843                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2844                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2845                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2846                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2847                file.close()
2848        finally:
2849            dlg.Destroy()
2850       
2851    def OnExportPDF(self,event):
2852        #need S(Q) and G(R) to be saved here - probably best from selection?
2853        names = ['All']
2854        exports = []
2855        item, cookie = self.PatternTree.GetFirstChild(self.root)
2856        while item:
2857            name = self.PatternTree.GetItemText(item)
2858            if 'PDF' in name:
2859                names.append(name)
2860            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2861        if names:
2862            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2863            if dlg.ShowModal() == wx.ID_OK:
2864                sel = dlg.GetSelections()
2865                if sel[0] == 0:
2866                    exports = names[1:]
2867                else:
2868                    for x in sel:
2869                        exports.append(names[x])
2870            dlg.Destroy()
2871        if exports:
2872            G2IO.PDFSave(self,exports)
2873       
2874    def OnMakePDFs(self,event):
2875        '''Calculates PDFs
2876        '''
2877        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2878        TextList = ['All PWDR']
2879        PDFlist = []
2880        Names = []
2881        if self.PatternTree.GetCount():
2882            id, cookie = self.PatternTree.GetFirstChild(self.root)
2883            while id:
2884                name = self.PatternTree.GetItemText(id)
2885                Names.append(name)
2886                if 'PWDR' in name:
2887                    TextList.append(name)
2888                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2889            if len(TextList) == 1:
2890                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2891                return
2892            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2893            try:
2894                if dlg.ShowModal() == wx.ID_OK:
2895                    result = dlg.GetSelections()
2896                    for i in result: PDFlist.append(TextList[i])
2897                    if 0 in result:
2898                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2899                    for item in PDFlist:
2900                        PWDRname = item[4:]
2901                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2902                        Data = {
2903                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2904                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2905                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2906                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2907                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2908                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2909                            'Lorch':True,}
2910                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2911                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2912                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2913                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2914                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2915                for item in self.ExportPDF: item.Enable(True)
2916            finally:
2917                dlg.Destroy()
2918               
2919    def GetPWDRdatafromTree(self,PWDRname):
2920        ''' Returns powder data from GSASII tree
2921
2922        :param str PWDRname: a powder histogram name as obtained from
2923          :meth:`GSASIIstruct.GetHistogramNames`
2924
2925        :returns: PWDRdata = powder data dictionary with
2926          Powder data arrays, Limits, Instrument Parameters,
2927          Sample Parameters           
2928        '''
2929        PWDRdata = {}
2930        try:
2931            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2932        except ValueError:
2933            PWDRdata['wtFactor'] = 1.0
2934        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2935        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2936        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2937        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2938        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2939        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2940        if 'ranId' not in PWDRdata:  # patch, add a random Id
2941            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
2942        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
2943            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
2944        return PWDRdata
2945
2946    def GetHKLFdatafromTree(self,HKLFname):
2947        ''' Returns single crystal data from GSASII tree
2948
2949        :param str HKLFname: a single crystal histogram name as obtained
2950          from
2951          :meth:`GSASIIstruct.GetHistogramNames`
2952
2953        :returns: HKLFdata = single crystal data list of reflections
2954
2955        '''
2956        HKLFdata = {}
2957        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2958#        try:
2959#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2960#        except ValueError:
2961#            HKLFdata['wtFactor'] = 1.0
2962        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2963        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2964        return HKLFdata
2965       
2966    def GetPhaseData(self):
2967        '''Returns a dict with defined phases.
2968        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
2969        get same info from GPX file.
2970        '''
2971        phaseData = {}
2972        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2973            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2974        else:
2975            print 'no phases found in GetPhaseData'
2976            sub = None
2977        if sub:
2978            item, cookie = self.PatternTree.GetFirstChild(sub)
2979            while item:
2980                phaseName = self.PatternTree.GetItemText(item)
2981                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2982                if 'ranId' not in phaseData[phaseName]:
2983                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2984                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2985        return phaseData
2986
2987    def GetPhaseInfofromTree(self):
2988        '''Get the phase names and their rId values,
2989        also the histograms used in each phase.
2990
2991        :returns: (phaseRIdList, usedHistograms) where
2992
2993          * phaseRIdList is a list of random Id values for each phase
2994          * usedHistograms is a dict where the keys are the phase names
2995            and the values for each key are a list of the histogram names
2996            used in each phase.
2997        '''
2998        phaseRIdList = []
2999        usedHistograms = {}
3000        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3001        if sub:
3002            item, cookie = self.PatternTree.GetFirstChild(sub)
3003            while item:
3004                phaseName = self.PatternTree.GetItemText(item)
3005                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3006                if ranId: phaseRIdList.append(ranId)
3007                data = self.PatternTree.GetItemPyData(item)
3008                UseList = data['Histograms']
3009                usedHistograms[phaseName] = UseList.keys()
3010                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3011        return phaseRIdList,usedHistograms
3012
3013    def GetPhaseNames(self):
3014        '''Returns a list of defined phases.
3015        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3016        get same info from GPX file.
3017        '''
3018        phaseNames = []
3019        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3020            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3021        else:
3022            print 'no phases found in GetPhaseNames'
3023            sub = None
3024        if sub:
3025            item, cookie = self.PatternTree.GetFirstChild(sub)
3026            while item:
3027                phase = self.PatternTree.GetItemText(item)
3028                phaseNames.append(phase)
3029                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3030        return phaseNames
3031   
3032    def GetHistogramNames(self,hType):
3033        """ Returns a list of histogram names found in the GSASII data tree
3034        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3035        get same info from GPX file.
3036       
3037        :param str hType: list of histogram types
3038        :return: list of histogram names
3039       
3040        """
3041        HistogramNames = []
3042        if self.PatternTree.GetCount():
3043            item, cookie = self.PatternTree.GetFirstChild(self.root)
3044            while item:
3045                name = self.PatternTree.GetItemText(item)
3046                if name[:4] in hType:
3047                    HistogramNames.append(name)       
3048                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3049
3050        return HistogramNames
3051
3052                   
3053    def GetUsedHistogramsAndPhasesfromTree(self):
3054        ''' Returns all histograms that are found in any phase
3055        and any phase that uses a histogram.
3056        This also assigns numbers to used phases and histograms by the
3057        order they appear in the file.
3058        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3059        get same info from GPX file.
3060
3061        :returns: (Histograms,Phases)
3062
3063            * Histograms = dictionary of histograms as {name:data,...}
3064            * Phases = dictionary of phases that use histograms
3065        '''
3066        Histograms = {}
3067        Phases = {}
3068        phaseNames = self.GetPhaseNames()
3069        phaseData = self.GetPhaseData()
3070        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3071
3072        for phase in phaseData:
3073            Phase = phaseData[phase]
3074            pId = phaseNames.index(phase)
3075            Phase['pId'] = pId
3076            if Phase['Histograms']:
3077                if phase not in Phases:
3078                    Phases[phase] = Phase
3079                for hist in Phase['Histograms']:
3080                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3081                        Phase['Histograms'][hist]['Use'] = True         
3082                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3083                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3084                        if item:
3085                            if 'PWDR' in hist[:4]: 
3086                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3087                            elif 'HKLF' in hist[:4]:
3088                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3089                            hId = histoList.index(hist)
3090                            Histograms[hist]['hId'] = hId
3091                        else: # would happen if a referenced histogram were renamed or deleted
3092                            print('For phase "'+str(phase)+
3093                                  '" unresolved reference to histogram "'+str(hist)+'"')
3094        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3095        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3096        return Histograms,Phases
3097       
3098    def MakeLSParmDict(self):
3099        '''Load all parameters used for computation from the tree into a
3100        dict of paired values [value, refine flag]. Note that this is
3101        different than the parmDict used in the refinement, which only has
3102        values.
3103
3104        Note that similar things are done in
3105        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3106        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3107        a GPX file).
3108
3109        :returns: (parmDict,varyList) where:
3110
3111         * parmDict is a dict with values and refinement flags
3112           for each parameter and
3113         * varyList is a list of variables (refined parameters).
3114        '''
3115        parmDict = {}
3116        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3117        for phase in Phases:
3118            if 'pId' not in Phases[phase]:
3119                self.ErrorDialog('View parameter error','You must run least squares at least once')
3120                raise Exception,'No pId for phase '+str(phase)
3121        rigidbodyDict = self.PatternTree.GetItemPyData(   
3122            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3123        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3124        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3125        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3126        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3127        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3128        varyList = rbVary+phaseVary+hapVary+histVary
3129        parmDict.update(rbDict)
3130        parmDict.update(phaseDict)
3131        parmDict.update(hapDict)
3132        parmDict.update(histDict)
3133        for parm in parmDict:
3134            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3135                'Omega','Chi','Phi','nDebye','nPeaks']:
3136                parmDict[parm] = [parmDict[parm],'-']
3137            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3138                parmDict[parm] = [parmDict[parm],'-']
3139            elif parm in varyList:
3140                parmDict[parm] = [parmDict[parm],'T']
3141            else:
3142                parmDict[parm] = [parmDict[parm],'F']
3143        # for i in parmDict: print i,'\t',parmDict[i]
3144        # fl = open('parmDict.dat','wb')
3145        # import cPickle
3146        # cPickle.dump(parmDict,fl,1)
3147        # fl.close()
3148        return parmDict,varyList
3149
3150    def ShowLSParms(self,event):
3151        '''Displays a window showing all parameters in the refinement.
3152        Called from the Calculate/View LS Parms menu.
3153        '''
3154        parmDict,varyList = self.MakeLSParmDict()
3155        parmValDict = {}
3156        for i in parmDict:
3157            parmValDict[i] = parmDict[i][0]
3158           
3159        reqVaryList = tuple(varyList) # save requested variables
3160        try:
3161            # process constraints
3162            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3163            Constraints = self.PatternTree.GetItemPyData(sub)
3164            constList = []
3165            for item in Constraints:
3166                if item.startswith('_'): continue
3167                constList += Constraints[item]
3168            G2mv.InitVars()
3169            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3170            groups,parmlist = G2mv.GroupConstraints(constrDict)
3171            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3172            G2mv.Map2Dict(parmValDict,varyList)
3173        except:
3174            pass
3175        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3176        dlg.ShowModal()
3177        dlg.Destroy()
3178       
3179    def OnRefine(self,event):
3180        '''Perform a refinement.
3181        Called from the Calculate/Refine menu.
3182        '''       
3183        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3184        if Id:
3185            dlg = wx.MessageDialog(
3186                self,
3187                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3188                'Remove sequential results?',wx.OK|wx.CANCEL)
3189            if dlg.ShowModal() == wx.ID_OK:
3190                self.PatternTree.Delete(Id)
3191                dlg.Destroy()
3192            else:
3193                dlg.Destroy()
3194                return
3195
3196        self.OnFileSave(event)
3197        # check that constraints are OK here
3198        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3199        if errmsg:
3200            print('Error in constraints:\n'+errmsg+
3201                  '\nRefinement not possible')
3202            self.ErrorDialog('Constraint Error',
3203                             'Error in constraints:\n'+errmsg+
3204                             '\nRefinement not possible')
3205            return
3206        if warnmsg:
3207            print('Conflict between refinment flag settings and constraints:\n'+
3208                  warnmsg+'\nRefinement not possible')
3209            self.ErrorDialog('Refinement Flag Error',
3210                             'Conflict between refinment flag settings and constraints:\n'+
3211                             warnmsg+
3212                             '\nRefinement not possible')
3213            return
3214        #works - but it'd be better if it could restore plots
3215        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3216            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3217        screenSize = wx.ClientDisplayRect()
3218        Size = dlg.GetSize()
3219        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3220        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3221        dlg.SetSize(Size)
3222        Rw = 100.00
3223        try:
3224            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3225        finally:
3226            dlg.Destroy()
3227        oldId =  self.PatternTree.GetSelection()        #retain current selection
3228        oldName = self.PatternTree.GetItemText(oldId)
3229        parentId = self.PatternTree.GetItemParent(oldId)
3230        parentName = ''
3231        if parentId:
3232            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3233        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3234        try:
3235            if dlg.ShowModal() == wx.ID_OK:
3236                Id = 0
3237                self.PatternTree.DeleteChildren(self.root)
3238                if self.HKL: self.HKL = []
3239                if self.G2plotNB.plotList:
3240                    self.G2plotNB.clear()
3241                G2IO.ProjFileOpen(self)
3242                item, cookie = self.PatternTree.GetFirstChild(self.root)
3243                while item and not Id:
3244                    name = self.PatternTree.GetItemText(item)
3245                    if name[:4] in ['PWDR','HKLF']:
3246                        Id = item
3247                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3248                if Id:
3249                    self.PatternTree.SelectItem(Id)
3250                if parentName:
3251                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3252                    if parentId:
3253                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3254                    else:
3255                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3256                    self.PatternTree.SelectItem(itemId)
3257        finally:
3258            dlg.Destroy()
3259
3260    def OnSeqRefine(self,event):
3261        '''Perform a sequential refinement.
3262        Called from the Calculate/Sequential refine menu.
3263        '''       
3264        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3265        if not Id:
3266            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3267            self.PatternTree.SetItemPyData(Id,{})           
3268        self.OnFileSave(event)
3269        # check that constraints are OK here
3270        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3271        if errmsg:
3272            print('Error in constraints:\n'+errmsg+
3273                  '\nRefinement not possible')
3274            self.ErrorDialog('Constraint Error',
3275                             'Error in constraints:\n'+errmsg+
3276                             '\nRefinement not possible')
3277            return
3278        if warnmsg:
3279            print('Conflict between refinment flag settings and constraints:\n'+
3280                  warnmsg+'\nRefinement not possible')
3281            self.ErrorDialog('Refinement Flag Error',
3282                             'Conflict between refinment flag settings and constraints:\n'+
3283                             warnmsg+'\nRefinement not possible')
3284            return
3285        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3286            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3287        screenSize = wx.ClientDisplayRect()
3288        Size = dlg.GetSize()
3289        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3290        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3291        dlg.SetSize(Size)
3292        try:
3293            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3294        finally:
3295            dlg.Destroy()       
3296        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3297        try:
3298            if dlg.ShowModal() == wx.ID_OK:
3299                Id = 0
3300                self.PatternTree.DeleteChildren(self.root)
3301                if self.HKL: self.HKL = []
3302                if self.G2plotNB.plotList:
3303                    self.G2plotNB.clear()
3304                G2IO.ProjFileOpen(self)
3305                Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3306                self.PatternTree.SelectItem(Id)
3307
3308        finally:
3309            dlg.Destroy()
3310       
3311    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3312        'Display an error message'
3313        result = None
3314        if parent is None:
3315            dlg = wx.MessageDialog(self, message, title,  wtype)
3316        else:
3317            dlg = wx.MessageDialog(parent, message, title,  wtype)
3318            dlg.CenterOnParent() # not working on Mac
3319        try:
3320            result = dlg.ShowModal()
3321        finally:
3322            dlg.Destroy()
3323        return result
3324
3325class GSASIImain(wx.App):
3326    '''Defines a wxApp for GSAS-II
3327
3328    Creates a wx frame (self.main) which contains the display of the
3329    data tree.
3330    '''
3331    def OnInit(self):
3332        '''Called automatically when the app is created.'''
3333        if '2.7' not in sys.version[:5]:
3334            dlg = wx.MessageDialog(None, 
3335                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version[:5],
3336                'Python version error',  wx.OK)
3337            try:
3338                result = dlg.ShowModal()
3339            finally:
3340                dlg.Destroy()
3341            sys.exit()
3342        self.main = GSASII(None)
3343        self.main.Show()
3344        self.SetTopWindow(self.main)
3345        # DEBUG: jump to sequential results
3346        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3347        #self.main.PatternTree.SelectItem(Id)
3348        # end DEBUG
3349        return True
3350    # def MacOpenFile(self, filename):
3351    #     '''Called on Mac every time a file is dropped on the app when it is running,
3352    #     treat this like a File/Open project menu action.
3353    #     Should be ignored on other platforms
3354    #     '''
3355    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3356    #     print 'MacOpen',filename
3357    #     if os.path.splitext(filename)[1] == '.py': return
3358    #     # end PATCH
3359    #     self.main.OnFileOpen(None,filename)
3360    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3361   
3362def main():
3363    '''Start up the GSAS-II application'''
3364    #application = GSASIImain() # don't redirect output, someday we
3365    # may want to do this if we can
3366    application = GSASIImain(0)
3367    if wxInspector:
3368        import wx.lib.inspection as wxeye
3369        wxeye.InspectionTool().Show()
3370
3371    #application.main.OnRefine(None)
3372    application.MainLoop()
3373   
3374if __name__ == '__main__':
3375    # print versions
3376    print "Python module versions loaded:"
3377    print "python:     ",sys.version[:5]
3378    print "wxpython:   ",wx.__version__
3379    print "matplotlib: ",mpl.__version__
3380    print "numpy:      ",np.__version__
3381    print "scipy:      ",sp.__version__
3382    print "OpenGL:     ",ogl.__version__
3383    try:
3384        from PIL import Image
3385        try:
3386            from PIL import PILLOW_VERSION
3387            version = PILLOW_VERSION
3388        except:
3389            version = Image.VERSION
3390        print "pillow:     ",version
3391    except ImportError:
3392        try:
3393            import Image
3394            print "Image (PIL):",Image.VERSION
3395        except ImportError:
3396            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3397    try:
3398        import mkl
3399        print "Max threads ",mkl.get_max_threads()
3400    except:
3401        pass
3402    import platform
3403    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3404    #print "wxPython description",wx.PlatformInfo
3405    print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
3406    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.