source: trunk/GSASII.py @ 2214

Last change on this file since 2214 was 2214, checked in by vondreele, 9 years ago

missing import copy

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