source: trunk/GSASII.py @ 2113

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

better cleanup when main window is closed
add FlagSetDialog? class & use it for wave parameters

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