source: trunk/GSASII.py @ 1657

Last change on this file since 1657 was 1657, checked in by toby, 7 years ago

migrate help from G2grid; set instparms in Powder imports; seach for imports in data path; start on tutorial migration

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