source: trunk/GSASII.py @ 2071

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

allow standalone testing of image importer from GSASIIIO.py

  • 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-11-25 20:41:43 +0000 (Wed, 25 Nov 2015) $
6# $Author: toby $
7# $Revision: 2071 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2071 2015-11-25 20:41:43Z toby $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. Restarting GSAS-II')
59    print('*******************************************************')         
60    loc = os.path.dirname(__file__)
61    import subprocess
62    subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
63    sys.exit()
64   
65# load the GSAS routines
66import GSASIIpath
67GSASIIpath.SetVersionNumber("$Revision: 2071 $")
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.PatternId = 0
2231        self.PickId = 0
2232        self.PickIdText = None
2233        self.PeakTable = []
2234        self.LimitsTable = []
2235        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2236        self.HKL = []
2237        self.Lines = []
2238        self.itemPicked = None
2239        self.dataFrame = None
2240        self.Interpolate = 'nearest'
2241        self.ContourColor = 'Paired'
2242        self.VcovColor = 'RdYlGn'
2243        self.RamaColor = 'Blues'
2244        self.Projection = 'equal area'
2245        self.logPlot = False
2246        self.sqPlot = False
2247        self.ErrorBars = False
2248        self.Contour = False
2249        self.Legend = False
2250        self.SinglePlot = True
2251        self.SubBack = False
2252        self.seqReverse = False
2253        self.seqLines = True #draw lines between points
2254        self.plotView = 0
2255        self.Image = 0
2256        self.oldImagefile = '' # the name of the last image file read
2257        self.oldImageTag = None # the name of the tag for multi-image files
2258        self.ImageZ = []  # this contains the image plotted and used for integration
2259        # self.ImageZ and self.oldImagefile are set in GSASIIplot.PlotImage
2260        # and GSASIIIO.GetImageData
2261        # any changes to self.ImageZ should initialize self.oldImagefile to force a reread
2262        self.Integrate = 0
2263        self.imageDefault = {}
2264        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2265        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2266        self.autoIntFrame = None
2267        self.IntegratedList = [] # list of already integrated IMG tree items
2268        self.Sngl = False
2269        self.ifGetRing = False
2270        self.MaskKey = ''           #trigger for making image masks
2271        self.StrainKey = ''         #ditto for new strain d-zeros
2272        self.EnablePlot = True
2273        self.hist = ''              # selected histogram in Phase/Data tab
2274        if GSASIIpath.GetConfigValue('Starting_directory'):
2275            try: 
2276                os.chdir(GSASIIpath.GetConfigValue('Starting_directory'))
2277            except:
2278                print('Ignoring Config Starting_directory value: '+
2279                      GSASIIpath.GetConfigValue('Starting_directory'))
2280        arg = sys.argv
2281        if len(arg) > 1 and arg[1]:
2282            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2283            self.dirname = os.path.dirname(arg[1])
2284            if self.dirname: os.chdir(self.dirname)
2285            try:
2286                self.StartProject()         #open the file if possible
2287            except Exception:
2288                print 'Error opening or reading file',arg[1]
2289                import traceback
2290                print traceback.format_exc()
2291             
2292        self.ImportDir = os.path.normpath(os.getcwd()) # specifies a default path to be used for imports
2293        if GSASIIpath.GetConfigValue('Import_directory'):
2294            self.ImportDir = GSASIIpath.GetConfigValue('Import_directory')
2295           
2296    def GetTreeItemsList(self,item):
2297        return self.PatternTree._getTreeItemsList(item)
2298
2299    def OnSize(self,event):
2300        'Called when the main window is resized. Not sure why'
2301        w,h = self.GetClientSizeTuple()
2302        self.mainPanel.SetSize(wx.Size(w,h))
2303        self.PatternTree.SetSize(wx.Size(w,h))
2304                       
2305    def OnPatternTreeSelChanged(self, event):
2306        '''Called when a data tree item is selected'''
2307        if self.TreeItemDelete:
2308            self.TreeItemDelete = False
2309        else:
2310            pltNum = self.G2plotNB.nb.GetSelection()
2311            if pltNum >= 0:                         #to avoid the startup with no plot!
2312                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2313                pltPlot = pltPage.figure
2314            item = event.GetItem()
2315            G2gd.MovePatternTreeToGrid(self,item)
2316            if self.oldFocus:
2317                self.oldFocus.SetFocus()
2318       
2319    def OnPatternTreeItemCollapsed(self, event):
2320        'Called when a tree item is collapsed - all children will be collapsed'
2321        self.PatternTree.CollapseAllChildren(event.GetItem())
2322
2323    def OnPatternTreeItemExpanded(self, event):
2324        'Called when a tree item is expanded'
2325        self.OnPatternTreeSelChanged(event)
2326        event.Skip()
2327       
2328    def OnPatternTreeItemDelete(self, event):
2329        'Called when a tree item is deleted -- not sure what this does'
2330        self.TreeItemDelete = True
2331
2332    def OnPatternTreeItemActivated(self, event):
2333        'Called when a tree item is activated'
2334        event.Skip()
2335       
2336    def OnPatternTreeBeginRDrag(self,event):
2337        event.Allow()
2338        self.BeginDragId = event.GetItem()
2339        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2340        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2341        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2342        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2343        while item:     #G2 data tree has no sub children under a child of a tree item
2344            name = self.PatternTree.GetItemText(item)
2345            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2346            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2347       
2348    def OnPatternTreeEndDrag(self,event):
2349        event.Allow()
2350        self.EndDragId = event.GetItem()
2351        try:
2352            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2353        except:
2354            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2355            NewParent = self.root
2356        if self.ParentId != NewParent:
2357            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2358        else:
2359            Name,Item = self.DragData[0]
2360            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2361            self.PatternTree.SetItemPyData(NewId,Item)
2362            for name,item in self.DragData[1:]:     #loop over children
2363                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2364                self.PatternTree.SetItemPyData(Id,item)
2365            self.PatternTree.Delete(self.BeginDragId)
2366            G2gd.MovePatternTreeToGrid(self,NewId)
2367       
2368    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2369        'Allows stepping through the tree with the up/down arrow keys'
2370        self.oldFocus = wx.Window.FindFocus()
2371        keyevt = event.GetKeyEvent()
2372        key = event.GetKeyCode()
2373        item = self.PatternTree.GetSelection()
2374        if type(item) is int: return # is this the toplevel in tree?
2375        name = self.PatternTree.GetItemText(item)
2376        parent = self.PatternTree.GetItemParent(item)
2377        if key == wx.WXK_UP:
2378            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2379                if type(parent) is int: return # is this the toplevel in tree?
2380                prev = self.PatternTree.GetPrevSibling(parent)
2381                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2382                if NewId:
2383                    self.PatternTree.Collapse(parent)
2384                    self.PatternTree.Expand(prev)
2385                    self.oldFocus = wx.Window.FindFocus()
2386                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2387                else:
2388                    wx.CallAfter(self.PatternTree.SelectItem,item)
2389            else:   
2390                self.PatternTree.GetPrevSibling(item)
2391                self.PatternTree.SelectItem(item)
2392        elif key == wx.WXK_DOWN:
2393            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2394                next = self.PatternTree.GetNextSibling(parent)
2395                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2396                if NewId:
2397                    self.PatternTree.Collapse(parent)
2398                    self.PatternTree.Expand(next)
2399                    self.oldFocus = wx.Window.FindFocus()
2400                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2401                else:
2402                    wx.CallAfter(self.PatternTree.SelectItem,item)
2403            else:   
2404                self.PatternTree.GetNextSibling(item)
2405                self.PatternTree.SelectItem(item)
2406               
2407    def OnReadPowderPeaks(self,event):
2408        'Bound to menu Data/Read Powder Peaks'
2409        Cuka = 1.54052
2410        self.CheckNotebook()
2411        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
2412            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
2413        try:
2414            if dlg.ShowModal() == wx.ID_OK:
2415                self.HKL = []
2416                self.powderfile = dlg.GetPath()
2417                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2418                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2419                data = ['PKS',wave,0.0]
2420                names = ['Type','Lam','Zero'] 
2421                codes = [0,0,0]
2422                inst = [G2IO.makeInstDict(names,data,codes),{}]
2423                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2424                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2425                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2426                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2427                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2428                self.PatternTree.Expand(Id)
2429                self.PatternTree.SelectItem(Id)
2430                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2431        finally:
2432            dlg.Destroy()
2433                       
2434    def OnImageRead(self,event):
2435        '''Called to read in an image in any known format. *** Depreciated. ***
2436        Note: When removed, G2IO.ReadLoadImage can also be removed
2437        '''
2438        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2439        self.CheckNotebook()
2440        dlg = wx.FileDialog(
2441            self, 'Choose image files', '.', '',
2442            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png)|'
2443            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png;*.zip|'
2444            'European detector file (*.edf)|*.edf|'
2445            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2446            'MAR file (*.mar*)|*.mar*|'
2447            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2448            'ADSC Image (*.img)|*.img|'
2449            'Rigaku R-Axis IV (*.stl)|*.stl|'
2450            'GSAS-II Image (*.G2img)|*.G2img|'
2451            'Portable Network Graphics image (*.png)|*.png|'
2452            'Zip archive (*.zip)|*.zip|'
2453            'All files (*.*)|*.*',
2454            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
2455        try:
2456            if dlg.ShowModal() == wx.ID_OK:
2457                imagefiles = dlg.GetPaths()
2458                imagefiles.sort()
2459                for imagefile in imagefiles:
2460                    G2IO.ReadLoadImage(imagefile,self)
2461                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2462                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,self.Image,'Image Controls'))             #show last image to be read
2463        finally:
2464            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2465            os.chdir(path)
2466            dlg.Destroy()
2467
2468    def CheckNotebook(self):
2469        '''Make sure the data tree has the minimally expected controls.
2470        '''
2471        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2472            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2473            self.PatternTree.SetItemPyData(sub,[''])
2474        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2475            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2476            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2477        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2478            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2479            self.PatternTree.SetItemPyData(sub,{})
2480        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2481            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2482            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2483        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2484            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2485            self.PatternTree.SetItemPyData(sub,{})
2486        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2487            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2488            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2489                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2490               
2491    class CopyDialog(wx.Dialog):
2492        '''Creates a dialog for copying control settings between
2493        data tree items'''
2494        def __init__(self,parent,title,text,data):
2495            wx.Dialog.__init__(self,parent,-1,title, 
2496                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2497            self.data = data
2498            panel = wx.Panel(self)
2499            mainSizer = wx.BoxSizer(wx.VERTICAL)
2500            topLabl = wx.StaticText(panel,-1,text)
2501            mainSizer.Add((10,10),1)
2502            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2503            mainSizer.Add((10,10),1)
2504            ncols = len(data)/40+1
2505            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2506            for id,item in enumerate(self.data):
2507                ckbox = wx.CheckBox(panel,id,item[1])
2508                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2509                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2510            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2511            OkBtn = wx.Button(panel,-1,"Ok")
2512            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2513            cancelBtn = wx.Button(panel,-1,"Cancel")
2514            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2515            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2516            btnSizer.Add((20,20),1)
2517            btnSizer.Add(OkBtn)
2518            btnSizer.Add((20,20),1)
2519            btnSizer.Add(cancelBtn)
2520            btnSizer.Add((20,20),1)
2521           
2522            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2523            panel.SetSizer(mainSizer)
2524            panel.Fit()
2525            self.Fit()
2526       
2527        def OnCopyChange(self,event):
2528            id = event.GetId()
2529            self.data[id][0] = self.FindWindowById(id).GetValue()       
2530           
2531        def OnOk(self,event):
2532            parent = self.GetParent()
2533            parent.Raise()
2534            self.EndModal(wx.ID_OK)             
2535           
2536        def OnCancel(self,event):
2537            parent = self.GetParent()
2538            parent.Raise()
2539            self.EndModal(wx.ID_CANCEL)             
2540           
2541        def GetData(self):
2542            return self.data
2543       
2544    class SumDialog(wx.Dialog):
2545        'Allows user to supply scale factor(s) when summing data'
2546        def __init__(self,parent,title,text,dataType,data):
2547            wx.Dialog.__init__(self,parent,-1,title, 
2548                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2549            self.data = data
2550            panel = wx.Panel(self)
2551            mainSizer = wx.BoxSizer(wx.VERTICAL)
2552            topLabl = wx.StaticText(panel,-1,text)
2553            mainSizer.Add((10,10),1)
2554            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2555            mainSizer.Add((10,10),1)
2556            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2557            for id,item in enumerate(self.data[:-1]):
2558                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2559                name.SetEditable(False)
2560                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2561                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2562                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2563                dataGridSizer.Add(scale,0,wx.LEFT,10)
2564                dataGridSizer.Add(name,0,wx.RIGHT,10)
2565            if dataType:
2566                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2567                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2568                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2569                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2570                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2571                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2572            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2573            OkBtn = wx.Button(panel,-1,"Ok")
2574            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2575            cancelBtn = wx.Button(panel,-1,"Cancel")
2576            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2577            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2578            btnSizer.Add((20,20),1)
2579            btnSizer.Add(OkBtn)
2580            btnSizer.Add((20,20),1)
2581            btnSizer.Add(cancelBtn)
2582            btnSizer.Add((20,20),1)
2583           
2584            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2585            panel.SetSizer(mainSizer)
2586            panel.Fit()
2587            self.Fit()
2588
2589        def OnScaleChange(self,event):
2590            id = event.GetId()
2591            value = self.FindWindowById(id).GetValue()
2592            try:
2593                self.data[id][0] = float(value)
2594                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2595            except ValueError:
2596                if value and '-' not in value[0]:
2597                    print 'bad input - numbers only'
2598                    self.FindWindowById(id).SetValue('0.000')
2599           
2600        def OnNameChange(self,event):
2601            self.data[-1] = self.name.GetValue() 
2602           
2603        def OnOk(self,event):
2604            parent = self.GetParent()
2605            parent.Raise()
2606            self.EndModal(wx.ID_OK)             
2607           
2608        def OnCancel(self,event):
2609            parent = self.GetParent()
2610            parent.Raise()
2611            self.EndModal(wx.ID_CANCEL)             
2612           
2613        def GetData(self):
2614            return self.data
2615                       
2616    def OnPwdrSum(self,event):
2617        'Sum together powder data(?)'
2618        TextList = []
2619        DataList = []
2620        SumList = []
2621        Names = []
2622        Inst = None
2623        SumItemList = []
2624        Comments = ['Sum equals: \n']
2625        if self.PatternTree.GetCount():
2626            item, cookie = self.PatternTree.GetFirstChild(self.root)
2627            while item:
2628                name = self.PatternTree.GetItemText(item)
2629                Names.append(name)
2630                if 'PWDR' in name:
2631                    TextList.append([0.0,name])
2632                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2633                    if not Inst:
2634                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2635                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2636            if len(TextList) < 2:
2637                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2638                return
2639            TextList.append('default_sum_name')               
2640            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2641            try:
2642                if dlg.ShowModal() == wx.ID_OK:
2643                    lenX = 0
2644                    Xminmax = [0,0]
2645                    Xsum = []
2646                    Ysum = []
2647                    Vsum = []
2648                    result = dlg.GetData()
2649                    for i,item in enumerate(result[:-1]):
2650                        scale,name = item
2651                        data = DataList[i]
2652                        if scale:
2653                            Comments.append("%10.3f %s" % (scale,' * '+name))
2654                            x,y,w,yc,yb,yd = data   #numpy arrays!
2655                            v = 1./w
2656                            if lenX:
2657                                if lenX != len(x):
2658                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2659                                        '\nExpected:'+str(lenX)+ \
2660                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2661                                    return
2662                            else:
2663                                lenX = len(x)
2664                            if Xminmax[1]:
2665                                if Xminmax != [x[0],x[-1]]:
2666                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2667                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2668                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2669                                    return
2670                                else:
2671                                    for j,yi in enumerate(y):
2672                                         Ysum[j] += scale*yi
2673                                         Vsum[j] += abs(scale)*v[j]
2674                            else:
2675                                Xminmax = [x[0],x[-1]]
2676                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2677                                for j,yi in enumerate(y):
2678                                    Xsum.append(x[j])
2679                                    Ysum.append(scale*yi)
2680                                    Vsum.append(abs(scale*v[j]))
2681                    Wsum = 1./np.array(Vsum)
2682                    outname = 'PWDR '+result[-1]
2683                    Id = 0
2684                    if outname in Names:
2685                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2686                        try:
2687                            if dlg2.ShowModal() == wx.ID_OK:
2688                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2689                                self.PatternTree.Delete(Id)
2690                        finally:
2691                            dlg2.Destroy()
2692                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2693                    if Id:
2694                        Sample = G2pdG.SetDefaultSample()
2695                        valuesdict = {
2696                            'wtFactor':1.0,
2697                            'Dummy':False,
2698                            'ranId':ran.randint(0,sys.maxint),
2699                            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
2700                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2701                            }
2702                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2703                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2704                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2705                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2706                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2707                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2708                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2709                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2710                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2711                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2712                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2713                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2714                        self.PatternTree.SelectItem(Id)
2715                        self.PatternTree.Expand(Id)
2716            finally:
2717                dlg.Destroy()
2718
2719    def OnImageSum(self,event):
2720        'Sum together image data(?)'
2721        TextList = []
2722        DataList = []
2723        SumList = []
2724        Names = []
2725        Inst = []
2726        SumItemList = []
2727        Comments = ['Sum equals: \n']
2728        if self.PatternTree.GetCount():
2729            item, cookie = self.PatternTree.GetFirstChild(self.root)
2730            while item:
2731                name = self.PatternTree.GetItemText(item)
2732                Names.append(name)
2733                if 'IMG' in name:
2734                    TextList.append([0.0,name])
2735                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2736                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2737                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2738            if len(TextList) < 2:
2739                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2740                return
2741            TextList.append('default_sum_name')               
2742            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2743            try:
2744                if dlg.ShowModal() == wx.ID_OK:
2745                    imSize = 0
2746                    result = dlg.GetData()
2747                    First = True
2748                    Found = False
2749                    for i,item in enumerate(result[:-1]):
2750                        scale,name = item
2751                        if scale:
2752                            Found = True                               
2753                            Comments.append("%10.3f %s" % (scale,' * '+name))
2754                            Npix,imagefile,imagetag = DataList[i]
2755                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2756                            if First:
2757                                newImage = np.zeros_like(image)
2758                                First = False
2759                            if imSize:
2760                                if imSize != Npix:
2761                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2762                                        '\nExpected:'+str(imSize)+ \
2763                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2764                                    return
2765                                newImage = newImage+scale*image
2766                            else:
2767                                imSize = Npix
2768                                newImage = newImage+scale*image
2769                            del(image)
2770                    if not Found:
2771                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2772                        return
2773                       
2774                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2775                    outname = 'IMG '+result[-1]
2776                    Id = 0
2777                    if outname in Names:
2778                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2779                        try:
2780                            if dlg2.ShowModal() == wx.ID_OK:
2781                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2782                        finally:
2783                            dlg2.Destroy()
2784                    else:
2785                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2786                    if Id:
2787                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2788                            'G2img files (*.G2img)|*.G2img', 
2789                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2790                        if dlg.ShowModal() == wx.ID_OK:
2791                            newimagefile = dlg.GetPath()
2792                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2793                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2794                            Imax = np.amax(newImage)
2795                            Imin = np.amin(newImage)
2796                            newImage = []
2797                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2798                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2799                        del(newImage)
2800                        if self.imageDefault:
2801                            Data = copy.copy(self.imageDefault)
2802                        Data['showLines'] = True
2803                        Data['ring'] = []
2804                        Data['rings'] = []
2805                        Data['cutoff'] = 10
2806                        Data['pixLimit'] = 20
2807                        Data['ellipses'] = []
2808                        Data['calibrant'] = ''
2809                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2810                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2811                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2812                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2813                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2814                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2815                        self.PatternTree.SelectItem(Id)
2816                        self.PatternTree.Expand(Id)
2817                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2818                        self.Image = self.PickId
2819            finally:
2820                dlg.Destroy()
2821                     
2822    def OnAddPhase(self,event):
2823        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2824        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2825            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2826        else:
2827            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2828        PhaseName = ''
2829        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2830            style=wx.OK)
2831        if dlg.ShowModal() == wx.ID_OK:
2832            PhaseName = dlg.GetValue()
2833        dlg.Destroy()
2834        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2835        E,SGData = G2spc.SpcGroup('P 1')
2836        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2837       
2838    def OnDeletePhase(self,event):
2839        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2840        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2841        if self.dataFrame:
2842            self.dataFrame.Clear() 
2843        TextList = []
2844        DelList = []
2845        DelItemList = []
2846        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2847            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2848        else:
2849            return
2850        if sub:
2851            item, cookie = self.PatternTree.GetFirstChild(sub)
2852            while item:
2853                TextList.append(self.PatternTree.GetItemText(item))
2854                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2855            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2856            try:
2857                if dlg.ShowModal() == wx.ID_OK:
2858                    result = dlg.GetSelections()
2859                    for i in result: DelList.append([i,TextList[i]])
2860                    item, cookie = self.PatternTree.GetFirstChild(sub)
2861                    i = 0
2862                    while item:
2863                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2864                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2865                        i += 1
2866                    for item in DelItemList:
2867                        name = self.PatternTree.GetItemText(item)
2868                        self.PatternTree.Delete(item)
2869                        self.G2plotNB.Delete(name)
2870                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2871                    while item:
2872                        name = self.PatternTree.GetItemText(item)
2873                        if 'PWDR' in name:
2874                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2875                            refList = self.PatternTree.GetItemPyData(Id)
2876                            if len(refList):
2877                                for i,item in DelList:
2878                                    if item in refList:
2879                                        del(refList[item])
2880                            self.PatternTree.SetItemPyData(Id,refList)
2881                        elif 'HKLF' in name:
2882                            data = self.PatternTree.GetItemPyData(item)
2883                            data[0] = {}
2884                            self.PatternTree.SetItemPyData(item,data)
2885                           
2886                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2887            finally:
2888                dlg.Destroy()
2889               
2890    def OnRenameData(self,event):
2891        'Renames an existing phase. Called by Data/Rename Phase menu'
2892        name = self.PatternTree.GetItemText(self.PickId)     
2893        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2894            dataType = name[:name.index(' ')+1]                 #includes the ' '
2895            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2896                defaultValue=name[name.index(' ')+1:])
2897            try:
2898                if dlg.ShowModal() == wx.ID_OK:
2899                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2900            finally:
2901                dlg.Destroy()
2902       
2903    def GetFileList(self,fileType,skip=None):        #potentially useful?
2904        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2905        fileList = []
2906        Source = ''
2907        id, cookie = self.PatternTree.GetFirstChild(self.root)
2908        while id:
2909            name = self.PatternTree.GetItemText(id)
2910            if fileType in name:
2911                if id == skip:
2912                    Source = name
2913                else:
2914                    fileList.append([False,name,id])
2915            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2916        if skip:
2917            return fileList,Source
2918        else:
2919            return fileList
2920           
2921    def OnDataDelete(self, event):
2922        '''Delete one or more histograms from data tree. Called by the
2923        Data/DeleteData menu
2924        '''
2925        TextList = []
2926        DelList = []
2927        DelItemList = []
2928        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
2929        ifPWDR = False
2930        ifSASD = False
2931        ifIMG = False
2932        ifHKLF = False
2933        ifPDF = False
2934        if self.PatternTree.GetCount():
2935            item, cookie = self.PatternTree.GetFirstChild(self.root)
2936            while item:
2937                name = self.PatternTree.GetItemText(item)
2938                if name not in ['Notebook','Controls','Covariance','Constraints',
2939                    'Restraints','Phases','Rigid bodies','Sequential results']:
2940                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
2941                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
2942                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
2943                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
2944                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
2945                    TextList.append(name)
2946                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2947            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2948            try:
2949                if dlg.ShowModal() == wx.ID_OK:
2950                    result = dlg.GetSelections()
2951                    for i in result: DelList.append(TextList[i])
2952                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2953                    while item:
2954                        itemName = self.PatternTree.GetItemText(item)
2955                        if itemName in DelList:
2956                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
2957                            elif 'SASD' in itemName: nItems['SASD'] -= 1
2958                            elif 'IMG' in itemName: nItems['IMG'] -= 1
2959                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
2960                            elif 'PDF' in itemName: nItems['PDF'] -= 1
2961                            DelItemList.append(item)
2962                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2963                    for item in DelItemList:
2964                        self.PatternTree.Delete(item)
2965                    self.PickId = 0
2966                    self.PickIdText = None
2967                    self.PatternId = 0
2968                    if nItems['PWDR']:
2969                        wx.CallAfter(G2plt.PlotPatterns,self,True)
2970                    else:
2971                        self.G2plotNB.Delete('Powder Patterns')
2972                    if not nItems['IMG']:
2973                        self.G2plotNB.Delete('2D Powder Image')
2974                    if not nItems['HKLF']:
2975                        self.G2plotNB.Delete('Structure Factors')
2976                        if '3D Structure Factors' in self.G2plotNB.plotList:
2977                            self.G2plotNB.Delete('3D Structure Factors')
2978            finally:
2979                dlg.Destroy()
2980
2981    def OnFileOpen(self, event, filename=None):
2982        '''Gets a GSAS-II .gpx project file in response to the
2983        File/Open Project menu button
2984        '''
2985        result = wx.ID_OK
2986        self.EnablePlot = False
2987        if self.PatternTree.GetChildrenCount(self.root,False):
2988            if self.dataFrame:
2989                self.dataFrame.Clear() 
2990            dlg = wx.MessageDialog(
2991                self,
2992                'Do you want to overwrite the current project? '
2993                'Any unsaved changes will be lost. Press OK to continue.',
2994                'Overwrite?',  wx.OK | wx.CANCEL)
2995            try:
2996                result = dlg.ShowModal()
2997                if result == wx.ID_OK:
2998                    self.PatternTree.DeleteChildren(self.root)
2999                    self.GSASprojectfile = ''
3000                    self.HKL = []
3001                    if self.G2plotNB.plotList:
3002                        self.G2plotNB.clear()
3003            finally:
3004                dlg.Destroy()
3005        if result != wx.ID_OK: return
3006
3007        if not filename:
3008            if self.dataDisplay: self.dataDisplay.Destroy()
3009            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
3010                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
3011            try:
3012                if dlg.ShowModal() != wx.ID_OK: return
3013                self.GSASprojectfile = dlg.GetPath()
3014                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3015                self.dirname = dlg.GetDirectory()
3016            finally:
3017                dlg.Destroy()
3018        else:
3019            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3020            self.dirname = os.path.split(filename)[0]
3021
3022        try:
3023            self.StartProject()         #open the file if possible
3024        except:
3025            print '\nError opening file ',filename
3026            import traceback
3027            print traceback.format_exc()
3028       
3029    def StartProject(self):
3030        '''Opens a GSAS-II project file & selects the 1st available data set to
3031        display (PWDR, HKLF or SASD)
3032        '''
3033       
3034        Id = 0
3035        G2IO.ProjFileOpen(self)
3036        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3037        self.PatternTree.Expand(self.root)
3038        self.HKL = []
3039        item, cookie = self.PatternTree.GetFirstChild(self.root)
3040        while item and not Id:
3041            name = self.PatternTree.GetItemText(item)
3042            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3043                Id = item
3044            elif name == 'Controls':
3045                data = self.PatternTree.GetItemPyData(item)
3046                if data:
3047                    for item in self.Refine: item.Enable(True)
3048                    self.EnableSeqRefineMenu()
3049            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3050        if Id:
3051            self.EnablePlot = True
3052            self.PatternTree.SelectItem(Id)
3053        self.CheckNotebook()
3054        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3055
3056    def OnFileClose(self, event):
3057        '''Clears the data tree in response to the
3058        File/New Project menu button. User is given option to save
3059        the project.
3060        '''
3061        if self.dataFrame:
3062            self.dataFrame.Clear()
3063            self.dataFrame.SetLabel('GSAS-II data display') 
3064        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3065        try:
3066            result = dlg.ShowModal()
3067            if result == wx.ID_OK:
3068                self.OnFileSaveMenu(event)
3069            if result != wx.ID_CANCEL:
3070                self.GSASprojectfile = ''
3071                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3072                self.PatternTree.DeleteChildren(self.root)
3073                if self.HKL: self.HKL = []
3074                if self.G2plotNB.plotList:
3075                    self.G2plotNB.clear()
3076        finally:
3077            dlg.Destroy()
3078
3079    def OnFileSave(self, event):
3080        '''Save the current project in response to the
3081        File/Save Project menu button
3082        '''
3083       
3084        if self.GSASprojectfile: 
3085            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3086            self.CheckNotebook()
3087            G2IO.ProjFileSave(self)
3088        else:
3089            self.OnFileSaveas(event)
3090
3091    def OnFileSaveas(self, event):
3092        '''Save the current project in response to the
3093        File/Save as menu button
3094        '''
3095        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
3096            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3097        try:
3098            if dlg.ShowModal() == wx.ID_OK:
3099                self.GSASprojectfile = dlg.GetPath()
3100                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3101                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3102                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3103                self.CheckNotebook()
3104                G2IO.ProjFileSave(self)
3105                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3106        finally:
3107            dlg.Destroy()
3108
3109    def ExitMain(self, event):
3110        '''Called if the main window is closed'''
3111        if self.undofile:
3112            os.remove(self.undofile)
3113        sys.exit()
3114       
3115    def OnFileExit(self, event):
3116        '''Called in response to the File/Quit menu button'''
3117        if self.dataFrame:
3118            self.dataFrame.Clear() 
3119            self.dataFrame.Destroy()
3120        self.Close()
3121       
3122    def OnExportPeakList(self,event):
3123        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
3124            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3125        try:
3126            if dlg.ShowModal() == wx.ID_OK:
3127                self.peaklistfile = dlg.GetPath()
3128                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3129                file = open(self.peaklistfile,'w')               
3130                item, cookie = self.PatternTree.GetFirstChild(self.root)
3131                while item:
3132                    name = self.PatternTree.GetItemText(item)
3133                    if 'PWDR' in name:
3134                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3135                        wave = 0.0
3136                        while item2:
3137                            name2 = self.PatternTree.GetItemText(item2)
3138                            if name2 == 'Instrument Parameters':
3139                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3140                                Type = Inst['Type'][0]
3141                                if 'T' not in Type:
3142                                    wave = G2mth.getWave(Inst)
3143                            elif name2 == 'Peak List':
3144                                peaks = self.PatternTree.GetItemPyData(item2)['peaks']
3145                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3146                        file.write("#%s \n" % (name+' Peak List'))
3147                        if wave:
3148                            file.write('#wavelength = %10.6f\n'%(wave))
3149                        if 'T' in Type:
3150                            file.write('#%9s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','int','alp','bet','sig','gam','FWHM'))                                   
3151                        else:
3152                            file.write('#%9s %10s %12s %10s %10s %10s\n'%('pos','dsp','int','sig','gam','FWHM'))
3153                        for peak in peaks:
3154                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3155                            if 'T' in Type:  #TOF - more cols
3156                                FWHM = 2.*G2pwd.getgamFW(peak[10],peak[8])      #to get delta-TOF from Gam(peak)
3157                                file.write("%10.2f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3158                                    (peak[0],dsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3159                            else:               #CW
3160                                FWHM = 2.*G2pwd.getgamFW(peak[6],peak[4])      #to get delta-2-theta in deg. from Gam(peak)
3161                                file.write("%10.3f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3162                                    (peak[0],dsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3163                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3164                file.close()
3165        finally:
3166            dlg.Destroy()
3167       
3168    def OnExportHKL(self,event):
3169        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
3170            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3171        try:
3172            if dlg.ShowModal() == wx.ID_OK:
3173                self.peaklistfile = dlg.GetPath()
3174                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3175                file = open(self.peaklistfile,'w')               
3176                item, cookie = self.PatternTree.GetFirstChild(self.root)
3177                while item:
3178                    name = self.PatternTree.GetItemText(item)
3179                    if 'PWDR' in name:
3180                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3181                        while item2:
3182                            name2 = self.PatternTree.GetItemText(item2)
3183                            if name2 == 'Reflection Lists':
3184                                data = self.PatternTree.GetItemPyData(item2)
3185                                phases = data.keys()
3186                                for phase in phases:
3187                                    peaks = data[phase]
3188                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3189                                    if 'T' in peaks.get('Type','PXC'):
3190                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3191                                    else:               
3192                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3193                                    for peak in peaks['RefList']:
3194                                        FWHM = 2.*G2pwd.getgamFW(peak[7],peak[6])
3195                                        if 'T' in peaks.get('Type','PXC'):
3196                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3197                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3198                                        else:
3199                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3200                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3201                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3202                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3203                file.close()
3204        finally:
3205            dlg.Destroy()
3206       
3207    def OnExportPDF(self,event):
3208        #need S(Q) and G(R) to be saved here - probably best from selection?
3209        names = ['All']
3210        exports = []
3211        item, cookie = self.PatternTree.GetFirstChild(self.root)
3212        while item:
3213            name = self.PatternTree.GetItemText(item)
3214            if 'PDF' in name:
3215                names.append(name)
3216            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3217        if names:
3218            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3219            if dlg.ShowModal() == wx.ID_OK:
3220                sel = dlg.GetSelections()
3221                if sel[0] == 0:
3222                    exports = names[1:]
3223                else:
3224                    for x in sel:
3225                        exports.append(names[x])
3226            dlg.Destroy()
3227        if exports:
3228            G2IO.PDFSave(self,exports)
3229       
3230    def OnMakePDFs(self,event):
3231        '''Calculates PDFs
3232        '''
3233        sind = lambda x: math.sin(x*math.pi/180.)
3234        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3235        TextList = ['All PWDR']
3236        PDFlist = []
3237        Names = []
3238        if self.PatternTree.GetCount():
3239            id, cookie = self.PatternTree.GetFirstChild(self.root)
3240            while id:
3241                name = self.PatternTree.GetItemText(id)
3242                Names.append(name)
3243                if 'PWDR' in name:
3244                    TextList.append(name)
3245                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3246            if len(TextList) == 1:
3247                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3248                return
3249            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3250            try:
3251                if dlg.ShowModal() == wx.ID_OK:
3252                    result = dlg.GetSelections()
3253                    for i in result: PDFlist.append(TextList[i])
3254                    if 0 in result:
3255                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3256                    for item in PDFlist:
3257                        PWDRname = item[4:]
3258                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3259                        Data = {
3260                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3261                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3262                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3263                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3264                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3265                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3266                            'Lorch':True,}
3267                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3268                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3269                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3270                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3271                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3272                for item in self.ExportPDF: item.Enable(True)
3273            finally:
3274                dlg.Destroy()
3275               
3276    def GetPWDRdatafromTree(self,PWDRname):
3277        ''' Returns powder data from GSASII tree
3278
3279        :param str PWDRname: a powder histogram name as obtained from
3280          :meth:`GSASIIstruct.GetHistogramNames`
3281
3282        :returns: PWDRdata = powder data dictionary with
3283          Powder data arrays, Limits, Instrument Parameters,
3284          Sample Parameters           
3285        '''
3286        PWDRdata = {}
3287        try:
3288            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3289        except ValueError:
3290            PWDRdata['wtFactor'] = 1.0
3291        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3292        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3293        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3294        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3295        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3296        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3297        if 'ranId' not in PWDRdata:  # patch, add a random Id
3298            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3299        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3300            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3301        return PWDRdata
3302
3303    def GetHKLFdatafromTree(self,HKLFname):
3304        ''' Returns single crystal data from GSASII tree
3305
3306        :param str HKLFname: a single crystal histogram name as obtained
3307          from
3308          :meth:`GSASIIstruct.GetHistogramNames`
3309
3310        :returns: HKLFdata = single crystal data list of reflections
3311
3312        '''
3313        HKLFdata = {}
3314        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3315#        try:
3316#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3317#        except ValueError:
3318#            HKLFdata['wtFactor'] = 1.0
3319        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3320        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3321        return HKLFdata
3322       
3323    def GetPhaseData(self):
3324        '''Returns a dict with defined phases.
3325        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3326        get same info from GPX file.
3327        '''
3328        phaseData = {}
3329        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3330            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3331        else:
3332            print 'no phases found in GetPhaseData'
3333            sub = None
3334        if sub:
3335            item, cookie = self.PatternTree.GetFirstChild(sub)
3336            while item:
3337                phaseName = self.PatternTree.GetItemText(item)
3338                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3339                if 'ranId' not in phaseData[phaseName]:
3340                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3341                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3342        return phaseData
3343
3344    def GetPhaseInfofromTree(self):
3345        '''Get the phase names and their rId values,
3346        also the histograms used in each phase.
3347
3348        :returns: (phaseRIdList, usedHistograms) where
3349
3350          * phaseRIdList is a list of random Id values for each phase
3351          * usedHistograms is a dict where the keys are the phase names
3352            and the values for each key are a list of the histogram names
3353            used in each phase.
3354        '''
3355        phaseRIdList = []
3356        usedHistograms = {}
3357        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3358        if sub:
3359            item, cookie = self.PatternTree.GetFirstChild(sub)
3360            while item:
3361                phaseName = self.PatternTree.GetItemText(item)
3362                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3363                if ranId: phaseRIdList.append(ranId)
3364                data = self.PatternTree.GetItemPyData(item)
3365                UseList = data['Histograms']
3366                usedHistograms[phaseName] = UseList.keys()
3367                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3368        return phaseRIdList,usedHistograms
3369
3370    def GetPhaseNames(self):
3371        '''Returns a list of defined phases.
3372        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3373        get same info from GPX file.
3374        '''
3375        phaseNames = []
3376        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3377            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3378        else:
3379            print 'no phases found in GetPhaseNames'
3380            sub = None
3381        if sub:
3382            item, cookie = self.PatternTree.GetFirstChild(sub)
3383            while item:
3384                phase = self.PatternTree.GetItemText(item)
3385                phaseNames.append(phase)
3386                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3387        return phaseNames
3388   
3389    def GetHistogramNames(self,hType):
3390        """ Returns a list of histogram names found in the GSASII data tree
3391        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3392        get same info from GPX file.
3393       
3394        :param str hType: list of histogram types
3395        :return: list of histogram names
3396       
3397        """
3398        HistogramNames = []
3399        if self.PatternTree.GetCount():
3400            item, cookie = self.PatternTree.GetFirstChild(self.root)
3401            while item:
3402                name = self.PatternTree.GetItemText(item)
3403                if name[:4] in hType:
3404                    HistogramNames.append(name)       
3405                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3406
3407        return HistogramNames
3408                   
3409    def GetUsedHistogramsAndPhasesfromTree(self):
3410        ''' Returns all histograms that are found in any phase
3411        and any phase that uses a histogram.
3412        This also assigns numbers to used phases and histograms by the
3413        order they appear in the file.
3414        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3415        get same info from GPX file.
3416
3417        :returns: (Histograms,Phases)
3418
3419            * Histograms = dictionary of histograms as {name:data,...}
3420            * Phases = dictionary of phases that use histograms
3421        '''
3422        Histograms = {}
3423        Phases = {}
3424        phaseNames = self.GetPhaseNames()
3425        phaseData = self.GetPhaseData()
3426        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3427
3428        for phase in phaseData:
3429            Phase = phaseData[phase]
3430            pId = phaseNames.index(phase)
3431            Phase['pId'] = pId
3432            if Phase['Histograms']:
3433                if phase not in Phases:
3434                    Phases[phase] = Phase
3435                for hist in Phase['Histograms']:
3436                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3437                        Phase['Histograms'][hist]['Use'] = True         
3438                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3439                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3440                        if item:
3441                            if 'PWDR' in hist[:4]: 
3442                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3443                            elif 'HKLF' in hist[:4]:
3444                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3445                            hId = histoList.index(hist)
3446                            Histograms[hist]['hId'] = hId
3447                        else: # would happen if a referenced histogram were renamed or deleted
3448                            print('For phase "'+str(phase)+
3449                                  '" unresolved reference to histogram "'+str(hist)+'"')
3450        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3451        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3452        return Histograms,Phases
3453       
3454    def MakeLSParmDict(self):
3455        '''Load all parameters used for computation from the tree into a
3456        dict of paired values [value, refine flag]. Note that this is
3457        different than the parmDict used in the refinement, which only has
3458        values.
3459
3460        Note that similar things are done in
3461        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3462        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3463        a GPX file).
3464
3465        :returns: (parmDict,varyList) where:
3466
3467         * parmDict is a dict with values and refinement flags
3468           for each parameter and
3469         * varyList is a list of variables (refined parameters).
3470        '''
3471        parmDict = {}
3472        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3473        for phase in Phases:
3474            if 'pId' not in Phases[phase]:
3475                self.ErrorDialog('View parameter error','You must run least squares at least once')
3476                raise Exception,'No pId for phase '+str(phase)
3477        rigidbodyDict = self.PatternTree.GetItemPyData(   
3478            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3479        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3480        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3481        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3482        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3483        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3484        varyList = rbVary+phaseVary+hapVary+histVary
3485        parmDict.update(rbDict)
3486        parmDict.update(phaseDict)
3487        parmDict.update(hapDict)
3488        parmDict.update(histDict)
3489        for parm in parmDict:
3490            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3491                'Omega','Chi','Phi','nDebye','nPeaks']:
3492                parmDict[parm] = [parmDict[parm],'-']
3493            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3494                parmDict[parm] = [parmDict[parm],'-']
3495            elif parm in varyList:
3496                parmDict[parm] = [parmDict[parm],'T']
3497            else:
3498                parmDict[parm] = [parmDict[parm],'F']
3499        # for i in parmDict: print i,'\t',parmDict[i]
3500        # fl = open('parmDict.dat','wb')
3501        # import cPickle
3502        # cPickle.dump(parmDict,fl,1)
3503        # fl.close()
3504        return parmDict,varyList
3505
3506    def ShowLSParms(self,event):
3507        '''Displays a window showing all parameters in the refinement.
3508        Called from the Calculate/View LS Parms menu.
3509        '''
3510        parmDict,varyList = self.MakeLSParmDict()
3511        parmValDict = {}
3512        for i in parmDict:
3513            parmValDict[i] = parmDict[i][0]
3514           
3515        reqVaryList = tuple(varyList) # save requested variables
3516        try:
3517            # process constraints
3518            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3519            Constraints = self.PatternTree.GetItemPyData(sub)
3520            constList = []
3521            for item in Constraints:
3522                if item.startswith('_'): continue
3523                constList += Constraints[item]
3524            G2mv.InitVars()
3525            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3526            groups,parmlist = G2mv.GroupConstraints(constrDict)
3527            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3528            G2mv.Map2Dict(parmValDict,varyList)
3529        except:
3530            pass
3531        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3532        dlg.ShowModal()
3533        dlg.Destroy()
3534       
3535    def OnRefine(self,event):
3536        '''Perform a refinement.
3537        Called from the Calculate/Refine menu.
3538        '''       
3539        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3540        if Id:
3541            dlg = wx.MessageDialog(
3542                self,
3543                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3544                'Remove sequential results?',wx.OK|wx.CANCEL)
3545            if dlg.ShowModal() == wx.ID_OK:
3546                self.PatternTree.Delete(Id)
3547                dlg.Destroy()
3548            else:
3549                dlg.Destroy()
3550                return
3551        self.OnFileSave(event)
3552        # check that constraints are OK here
3553        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3554        if errmsg:
3555            self.ErrorDialog('Refinement error',errmsg)
3556            return
3557        if warnmsg:
3558            print('Conflict between refinment flag settings and constraints:\n'+
3559                warnmsg+'\nRefinement not possible')
3560            self.ErrorDialog('Refinement Flag Error',
3561                'Conflict between refinement flag settings and constraints:\n'+
3562                warnmsg+'\nRefinement not possible')
3563            return
3564        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3565            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3566            parent=self)
3567        Size = dlg.GetSize()
3568        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3569            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3570        dlg.CenterOnParent()
3571        Rw = 100.00
3572        oldId =  self.PatternTree.GetSelection()        #retain current selection
3573        oldPath = self.GetTreeItemsList(oldId)
3574        parentName = ''
3575        oldName = self.PatternTree.GetItemText(oldId)
3576        parentId = self.PatternTree.GetItemParent(oldId)
3577        if parentId:
3578            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3579            if 'Phases' in parentName:
3580                tabId = self.dataDisplay.GetSelection()
3581        try:
3582            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)
3583        finally:
3584            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3585            dlg.Destroy()
3586            wx.Yield()
3587        if OK:
3588            Rw = Msg
3589            dlg2 = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3590            try:
3591                if dlg2.ShowModal() == wx.ID_OK:
3592                    Id = 0
3593                    self.PatternTree.DeleteChildren(self.root)
3594                    self.HKL = []
3595                    G2IO.ProjFileOpen(self)
3596                    Id =  self.root
3597                    txt = None
3598                    for txt in oldPath:
3599                        Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3600                    self.PickIdText = None  #force reload of page
3601                    self.PickId = Id
3602                    self.PatternTree.SelectItem(Id)
3603                    G2gd.MovePatternTreeToGrid(self,Id)
3604            finally:
3605                dlg2.Destroy()
3606        else:
3607            self.ErrorDialog('Refinement error',Msg)
3608
3609    def OnSeqRefine(self,event):
3610        '''Perform a sequential refinement.
3611        Called from the Calculate/Sequential refine menu.
3612        '''       
3613        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3614        if not Id:
3615            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3616            self.PatternTree.SetItemPyData(Id,{})           
3617        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3618        Controls['ShowCell'] = True
3619        self.OnFileSave(event)
3620        # check that constraints are OK here
3621        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3622        if errmsg:
3623            self.ErrorDialog('Refinement error',errmsg)
3624            return
3625        if warnmsg:
3626            print('Conflict between refinment flag settings and constraints:\n'+
3627                  warnmsg+'\nRefinement not possible')
3628            self.ErrorDialog('Refinement Flag Error',
3629                             'Conflict between refinment flag settings and constraints:\n'+
3630                             warnmsg+'\nRefinement not possible')
3631            return
3632        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3633            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3634            parent=self)           
3635        Size = dlg.GetSize()
3636        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3637            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3638        dlg.CenterOnParent()
3639        try:
3640            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3641        finally:
3642            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3643            dlg.Destroy()
3644            wx.Yield()
3645        if OK:
3646            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3647            try:
3648                if dlg.ShowModal() == wx.ID_OK:
3649                    Id = 0
3650                    self.PickIdText = None  #force reload of PickId contents
3651                    self.PatternTree.DeleteChildren(self.root)
3652                    if len(self.HKL): self.HKL = []
3653                    if self.G2plotNB.plotList:
3654                        self.G2plotNB.clear()
3655                    G2IO.ProjFileOpen(self)
3656                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3657                    self.PatternTree.SelectItem(Id)
3658   
3659            finally:
3660                dlg.Destroy()
3661        else:
3662            self.ErrorDialog('Sequential refinement error',Msg)
3663       
3664    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3665        'Display an error message'
3666        result = None
3667        if parent is None:
3668            dlg = wx.MessageDialog(self, message, title,  wtype)
3669        else:
3670            dlg = wx.MessageDialog(parent, message, title,  wtype)
3671            dlg.CenterOnParent() # not working on Mac
3672        try:
3673            result = dlg.ShowModal()
3674        finally:
3675            dlg.Destroy()
3676        return result
3677
3678class GSASIImain(wx.App):
3679    '''Defines a wxApp for GSAS-II
3680
3681    Creates a wx frame (self.main) which contains the display of the
3682    data tree.
3683    '''
3684    def OnInit(self):
3685        '''Called automatically when the app is created.'''
3686        import platform
3687        if '2.7' not in sys.version[:5]:
3688            dlg = wx.MessageDialog(None, 
3689                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
3690                'Python version error',  wx.OK)
3691            try:
3692                result = dlg.ShowModal()
3693            finally:
3694                dlg.Destroy()
3695            sys.exit()
3696        self.main = GSASII(None)
3697        self.main.Show()
3698        self.SetTopWindow(self.main)
3699        # save the current package versions
3700        self.main.PackageVersions = []
3701        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
3702        for p in (wx,mpl,np,sp,ogl):
3703            self.main.PackageVersions.append([p.__name__,p.__version__])
3704        try:
3705            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
3706        except:
3707            try:
3708                from PIL import PILLOW_VERSION
3709                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
3710            except:
3711                pass
3712        self.main.PackageVersions.append([' Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
3713       
3714#        self.main.PackageVersions = {}
3715#        self.main.PackageVersions['Python'] = sys.version.split()[0]
3716#        for p in (wx,mpl,np,sp,ogl):
3717#            self.main.PackageVersions[p.__name__] = p.__version__
3718#        try:
3719#            self.main.PackageVersions[Image.__name__] = Image.VERSION
3720#        except:
3721#            try:
3722#                from PIL import PILLOW_VERSION
3723#                self.main.PackageVersions[Image.__name__] = PILLOW_VERSION
3724#            except:
3725#                pass
3726#        self.main.PackageVersions[' Platform'] = sys.platform+' '+platform.architecture()[0]+' '+platform.machine()
3727        # DEBUG: jump to sequential results
3728        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3729        #self.main.PatternTree.SelectItem(Id)
3730        # end DEBUG
3731        return True
3732    # def MacOpenFile(self, filename):
3733    #     '''Called on Mac every time a file is dropped on the app when it is running,
3734    #     treat this like a File/Open project menu action.
3735    #     Should be ignored on other platforms
3736    #     '''
3737    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3738    #     print 'MacOpen',filename
3739    #     if os.path.splitext(filename)[1] == '.py': return
3740    #     # end PATCH
3741    #     self.main.OnFileOpen(None,filename)
3742    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3743       
3744def main():
3745    '''Start up the GSAS-II application'''
3746    #application = GSASIImain() # don't redirect output, someday we
3747    # may want to do this if we can
3748    application = GSASIImain(0)
3749    if GSASIIpath.GetConfigValue('wxInspector'):
3750        import wx.lib.inspection as wxeye
3751        wxeye.InspectionTool().Show()
3752
3753    #application.main.OnRefine(None)
3754    application.MainLoop()
3755   
3756if __name__ == '__main__':
3757    # print versions
3758    print "Python module versions loaded:"
3759    print "python:     ",sys.version.split()[0]
3760    print "wxpython:   ",wx.__version__
3761    print "matplotlib: ",mpl.__version__
3762    print "numpy:      ",np.__version__
3763    print "scipy:      ",sp.__version__
3764    print "OpenGL:     ",ogl.__version__
3765    try:
3766        from PIL import Image
3767        try:
3768            from PIL import PILLOW_VERSION
3769            version = PILLOW_VERSION
3770        except:
3771            version = Image.VERSION
3772        print "pillow:     ",version
3773    except ImportError:
3774        try:
3775            import Image
3776            print "Image (PIL):",Image.VERSION
3777        except ImportError:
3778            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3779    try:
3780        import mkl
3781        print "Max threads ",mkl.get_max_threads()
3782    except:
3783        pass
3784    import platform
3785    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3786    #print "wxPython description",wx.PlatformInfo
3787    print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
3788    GSASIIpath.InvokeDebugOpts()
3789    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.