source: trunk/GSASII.py @ 2077

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

refactor wave data display to do one atom at a time instead of a long GUI

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