source: trunk/GSASII.py @ 1713

Last change on this file since 1713 was 1713, checked in by vondreele, 7 years ago

fix problems with imports of old TOF data; bank nos. now are as given in parm/data file rather than starting with 1...

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