source: trunk/GSASII.py @ 1606

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

more FillAtomLookUp? errors
a few more orthorhombic SS symbols
some fixes for missing/deleted phases

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