source: trunk/GSASII.py @ 1743

Last change on this file since 1743 was 1743, checked in by vondreele, 8 years ago

fix to data selection issue
formatting error for histogram scale
change ':' to ';' for SASD parameter names

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