source: trunk/GSASII.py @ 2083

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

Export multiple masks/controls

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