source: trunk/GSASII.py @ 1696

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

more work on seq. ref. with differing variables; rework svn interface; fix tutorial downloads

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