source: trunk/GSASII.py @ 1913

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

fix sfact importers so twin id=1 by default
GUI details for twins fixed

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