source: trunk/GSASII.py @ 1399

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

allow D (&T) for hydrogen atom types
allow ge* for image extensions (as found at APS 1ID!)
switch e11 & e22 definitions to match coordinate system (x horizontal, y vertical)
add subtraction of dark image for image display & strain fitting
skip images with no strain entries in fitallstrsta

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