source: trunk/GSASII.py @ 2101

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

Drop #1 from header in one-image files; revise HDF5 importer to read in slices; add .csv PWDR importer

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