source: trunk/GSASII.py @ 1390

Last change on this file since 1390 was 1390, checked in by toby, 8 years ago

revise start, sort out phase fraction vs scale factor label, add configurable label for columns in sequential refinements. Note: the dict (variableLabels) is not saved yet when a seq. refinement is repeated.

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