source: trunk/GSASII.py @ 1486

Last change on this file since 1486 was 1486, checked in by toby, 9 years ago

minor bugs

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