source: trunk/GSASII.py @ 1698

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

Allow powder data importers to set Controls values (such as FreePrm? labels); fix FreePrm? initialization

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 172.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2015-03-12 22:09:00 +0000 (Thu, 12 Mar 2015) $
6# $Author: toby $
7# $Revision: 1698 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1698 2015-03-12 22:09:00Z 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: 1698 $")
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            # if any Control values have been set, move them into tree
1366            Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
1367            Controls.update(rd.Controls)
1368            newHistList.append(HistName)
1369        else:
1370            self.EnablePlot = True
1371            self.PatternTree.Expand(Id)
1372            self.PatternTree.SelectItem(Id)
1373           
1374        if not newHistList: return # somehow, no new histograms
1375        # make a list of phase names
1376        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1377        phaseNameList = usedHistograms.keys() # phase names in use
1378        if not phaseNameList: return # no phases yet, nothing to do
1379        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1380        for Name in newHistList:
1381            header += '\n  '+str(Name)
1382
1383        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1384        if not result: return
1385        # connect new phases to histograms
1386        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1387        if not sub:
1388            raise Exception('ERROR -- why are there no phases here?')
1389        item, cookie = self.PatternTree.GetFirstChild(sub)
1390        iph = -1
1391        while item: # loop over (new) phases
1392            iph += 1
1393            phaseName = self.PatternTree.GetItemText(item)
1394            data = self.PatternTree.GetItemPyData(item)
1395            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1396            if iph not in result: continue
1397            generalData = data['General']
1398            SGData = generalData['SGData']
1399            UseList = data['Histograms']
1400            NShkl = len(G2spc.MustrainNames(SGData))
1401            NDij = len(G2spc.HStrainNames(SGData))
1402            for histoName in newHistList:
1403                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1404                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1405                refList = self.PatternTree.GetItemPyData(
1406                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1407                refList[generalData['Name']] = []
1408        return # success
1409
1410    def OnDummyPowder(self,event):
1411        '''Called in response to Import/Powder Data/Simulate menu item
1412        to create a Dummy powder diffraction data set.
1413
1414        Reads an instrument parameter file and then gets input from the user
1415        '''
1416        # get a list of existing histograms
1417        PWDRlist = []
1418        if self.PatternTree.GetCount():
1419            item, cookie = self.PatternTree.GetFirstChild(self.root)
1420            while item:
1421                name = self.PatternTree.GetItemText(item)
1422                if name.startswith('PWDR ') and name not in PWDRlist:
1423                    PWDRlist.append(name)
1424                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1425        # Initialize a base class reader
1426        rd = G2IO.ImportPowderData(
1427            extensionlist=tuple(),
1428            strictExtension=False,
1429            formatName = 'Simulate dataset',
1430            longFormatName = 'Compute a simulated pattern')
1431        rd.powderentry[0] = '' # no filename
1432        # #self.powderentry[1] = pos # bank offset (N/A here)
1433        rd.powderentry[2] = 1 # only one bank
1434        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1435        self.CheckNotebook()
1436        Iparm = None
1437        lastIparmfile = ''
1438        lastdatafile = ''
1439        self.zipfile = None
1440        # get instrument parameters for it
1441        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1442        if 'T' in Iparm1['Type'][0]:
1443            print('TOF simulation not supported yet')
1444            return False
1445        else:
1446            # need to get name, 2theta start, end, step
1447            rd.idstring = ' CW'
1448            if 'X' in Iparm1['Type'][0]:
1449                rd.idstring = 'CW x-ray simulation'
1450            else:
1451                rd.idstring = 'CW neutron simulation'
1452            # base initial range on wavelength
1453            wave = Iparm1.get('Lam')
1454            if wave:
1455                wave = wave[0]
1456            else:
1457                wave = Iparm1.get('Lam1')
1458                if wave:
1459                    wave = wave[0]
1460        N = 0
1461        while (N < 3): # insist on a dataset with a few points
1462            names = ('dataset name', 'start angle', 'end angle', 'step size')
1463            if not wave or wave < 1.0:
1464                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1465            else:
1466                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1467            dlg = G2G.ScrolledMultiEditor(
1468                self,[inp] * len(inp),range(len(inp)),names,
1469                header='Enter simulation name and range',
1470                minvals=(None,0.001,0.001,0.0001),
1471                maxvals=(None,180.,180.,.1),
1472                sizevals=((225,-1),)
1473                )
1474            dlg.CenterOnParent()
1475            if dlg.ShowModal() == wx.ID_OK:
1476                if inp[1] > inp[2]:
1477                    end,start,step = inp[1:]
1478                else:               
1479                    start,end,step = inp[1:]
1480                step = abs(step)
1481            else:
1482                return False
1483            N = int((end-start)/step)+1
1484            x = np.linspace(start,end,N,True)
1485            N = len(x)
1486        rd.powderdata = [
1487            np.array(x), # x-axis values
1488            np.zeros_like(x), # powder pattern intensities
1489            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1490            np.zeros_like(x), # calc. intensities (zero)
1491            np.zeros_like(x), # calc. background (zero)
1492            np.zeros_like(x), # obs-calc profiles
1493            ]
1494        Tmin = rd.powderdata[0][0]
1495        Tmax = rd.powderdata[0][-1]
1496        # data are read, now store them in the tree
1497        HistName = inp[0]
1498        HistName = 'PWDR '+HistName
1499        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1500        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1501        valuesdict = {
1502            'wtFactor':1.0,
1503            'Dummy':True,
1504            'ranId':ran.randint(0,sys.maxint),
1505            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1506            'qPlot':False,'dPlot':False,'sqrtPlot':False
1507            }
1508        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1509        self.PatternTree.SetItemPyData(
1510            self.PatternTree.AppendItem(Id,text='Comments'),
1511            rd.comments)
1512        self.PatternTree.SetItemPyData(
1513            self.PatternTree.AppendItem(Id,text='Limits'),
1514            [(Tmin,Tmax),[Tmin,Tmax]])
1515        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1516        self.PatternTree.SetItemPyData(
1517            self.PatternTree.AppendItem(Id,text='Background'),
1518            [['chebyschev',True,3,1.0,0.0,0.0],
1519             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1520        self.PatternTree.SetItemPyData(
1521            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1522            [Iparm1,Iparm2])
1523        self.PatternTree.SetItemPyData(
1524            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1525            rd.Sample)
1526        self.PatternTree.SetItemPyData(
1527            self.PatternTree.AppendItem(Id,text='Peak List')
1528            ,{'peaks':[],'sigDict':{}})
1529        self.PatternTree.SetItemPyData(
1530            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1531            [[],[]])
1532        self.PatternTree.SetItemPyData(
1533            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1534            [])
1535        self.PatternTree.SetItemPyData(
1536            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1537            {})
1538        self.PatternTree.Expand(Id)
1539        self.PatternTree.SelectItem(Id)
1540        print('Added simulation powder data '+str(HistName)+ 
1541              ' with parameters from '+str(rd.instmsg))
1542
1543        # make a list of phase names
1544        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1545        phaseNameList = usedHistograms.keys() # phase names in use
1546        if not phaseNameList: return # no phases yet, nothing to do
1547        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1548        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1549        if not result: return
1550        # connect new phases to histograms
1551        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1552        if not sub:
1553            raise Exception('ERROR -- why are there no phases here?')
1554        item, cookie = self.PatternTree.GetFirstChild(sub)
1555        iph = -1
1556        while item: # loop over (new) phases
1557            iph += 1
1558            phaseName = self.PatternTree.GetItemText(item)
1559            data = self.PatternTree.GetItemPyData(item)
1560            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1561            if iph not in result: continue
1562            generalData = data['General']
1563            SGData = generalData['SGData']
1564            UseList = data['Histograms']
1565            NShkl = len(G2spc.MustrainNames(SGData))
1566            NDij = len(G2spc.HStrainNames(SGData))
1567            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
1568            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1569            refList = self.PatternTree.GetItemPyData(
1570                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1571            refList[generalData['Name']] = []
1572        return # success
1573
1574    def _Add_ImportMenu_smallangle(self,parent):
1575        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1576        '''
1577        submenu = wx.Menu()
1578        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1579            submenu, help='Import small angle data')
1580        for reader in self.ImportSmallAngleReaderlist:
1581            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1582                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1583            self.ImportMenuId[item.GetId()] = reader
1584            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1585        # item = submenu.Append(wx.ID_ANY,
1586        #     help='Import small angle data, use file to try to determine format',
1587        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1588        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1589
1590    def OnImportSmallAngle(self,event):
1591        '''Called in response to an Import/Small Angle Data/... menu item
1592        to read a small angle diffraction data set.
1593        dict self.ImportMenuId is used to look up the specific
1594        reader item associated with the menu item, which will be
1595        None for the last menu item, which is the "guess" option
1596        where all appropriate formats will be tried.
1597
1598        '''
1599       
1600        def GetSASDIparm(reader):
1601            parm = reader.instdict
1602            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1603                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1604            return Iparm,{}
1605           
1606        # get a list of existing histograms
1607        SASDlist = []
1608        if self.PatternTree.GetCount():
1609            item, cookie = self.PatternTree.GetFirstChild(self.root)
1610            while item:
1611                name = self.PatternTree.GetItemText(item)
1612                if name.startswith('SASD ') and name not in SASDlist:
1613                    SASDlist.append(name)
1614                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1615        # look up which format was requested
1616        reqrdr = self.ImportMenuId.get(event.GetId()) 
1617        rdlist = self.OnImportGeneric(
1618            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1619        if len(rdlist) == 0: return
1620        self.CheckNotebook()
1621        Iparm = None
1622        lastdatafile = ''
1623        newHistList = []
1624        self.EnablePlot = False
1625        for rd in rdlist:
1626            lastdatafile = rd.smallangleentry[0]
1627            HistName = rd.idstring
1628            HistName = 'SASD '+HistName
1629            # make new histogram names unique
1630            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1631            print 'Read small angle data '+str(HistName)+ \
1632                ' from file '+str(self.lastimport)
1633            # data are read, now store them in the tree
1634            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1635            Iparm1,Iparm2 = GetSASDIparm(rd)
1636#            if 'T' in Iparm1['Type'][0]:
1637#                if not rd.clockWd and rd.GSAS:
1638#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1639#                cw = np.diff(rd.powderdata[0])
1640#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1641#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1642#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1643#                if 'Itype' in Iparm2:
1644#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1645#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1646#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1647#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1648#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1649#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1650#                    var += WYI*rd.powderdata[1]**2
1651#                    var /= YI**2
1652#                    rd.powderdata[2] = 1./var
1653#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1654#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1655#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1656            Tmin = min(rd.smallangledata[0])
1657            Tmax = max(rd.smallangledata[0])
1658            valuesdict = {
1659                'wtFactor':1.0,
1660                'Dummy':False,
1661                'ranId':ran.randint(0,sys.maxint),
1662                }
1663            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1664            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1665            self.PatternTree.SetItemPyData(
1666                self.PatternTree.AppendItem(Id,text='Comments'),
1667                rd.comments)
1668            self.PatternTree.SetItemPyData(
1669                self.PatternTree.AppendItem(Id,text='Limits'),
1670                [(Tmin,Tmax),[Tmin,Tmax]])
1671            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1672            self.PatternTree.SetItemPyData(
1673                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1674                [Iparm1,Iparm2])
1675            self.PatternTree.SetItemPyData(
1676                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1677            self.PatternTree.SetItemPyData(
1678                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1679                rd.Sample)
1680            self.PatternTree.SetItemPyData(
1681                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1682            newHistList.append(HistName)
1683        else:
1684            self.EnablePlot = True
1685            self.PatternTree.Expand(Id)
1686            self.PatternTree.SelectItem(Id)
1687           
1688        if not newHistList: return # somehow, no new histograms
1689        return # success
1690
1691    def OnMacroRecordStatus(self,event,setvalue=None):
1692        '''Called when the record macro menu item is used which toggles the
1693        value. Alternately a value to be set can be provided. Note that this
1694        routine is made more complex because on the Mac there are lots of menu
1695        items (listed in self.MacroStatusList) and this loops over all of them.
1696        '''
1697        nextvalue = log.ShowLogStatus() != True
1698        if setvalue is not None:
1699            nextvalue = setvalue
1700        if nextvalue:
1701            log.LogOn()
1702            set2 = True
1703        else:
1704            log.LogOff()
1705            set2 = False
1706        for menuitem in self.MacroStatusList:
1707            menuitem.Check(set2)
1708
1709    def _init_Macro(self):
1710        '''Define the items in the macro menu.
1711        '''
1712        menu = self.MacroMenu
1713        item = menu.Append(
1714                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
1715                kind=wx.ITEM_CHECK,text='Record actions')
1716        self.MacroStatusList.append(item)
1717        item.Check(log.ShowLogStatus())
1718        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
1719
1720        # this may only be of value for development work
1721        item = menu.Append(
1722            help='Show logged commands', id=wx.ID_ANY,
1723            kind=wx.ITEM_NORMAL,text='Show log')
1724        def OnShowLog(event):
1725            print 70*'='
1726            print 'List of logged actions'
1727            for i,line in enumerate(log.G2logList):
1728                if line: print i,line
1729            print 70*'='
1730        self.Bind(wx.EVT_MENU, OnShowLog, item)
1731
1732        item = menu.Append(
1733            help='Clear logged commands', id=wx.ID_ANY,
1734            kind=wx.ITEM_NORMAL,text='Clear log')
1735        def OnClearLog(event): log.G2logList=[None]
1736        self.Bind(wx.EVT_MENU, OnClearLog, item)
1737       
1738        item = menu.Append(
1739            help='Save logged commands to file', id=wx.ID_ANY,
1740            kind=wx.ITEM_NORMAL,text='Save log')
1741        def OnSaveLog(event):
1742            import cPickle
1743            defnam = os.path.splitext(
1744                os.path.split(self.GSASprojectfile)[1]
1745                )[0]+'.gcmd'
1746            dlg = wx.FileDialog(self,
1747                'Choose an file to save past actions', '.', defnam, 
1748                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1749                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1750            dlg.CenterOnParent()
1751            try:
1752                if dlg.ShowModal() == wx.ID_OK:
1753                    filename = dlg.GetPath()
1754                    # make sure extension is correct
1755                    filename = os.path.splitext(filename)[0]+'.gcmd'
1756                else:
1757                    filename = None
1758            finally:
1759                dlg.Destroy()
1760            if filename:
1761                fp = open(filename,'wb')
1762                fp.write(str(len(log.G2logList)-1)+'\n')
1763                for item in log.G2logList:
1764                    if item: cPickle.dump(item,fp)
1765                fp.close()
1766        self.Bind(wx.EVT_MENU, OnSaveLog, item)
1767
1768        item = menu.Append(
1769            help='Load logged commands from file', id=wx.ID_ANY,
1770            kind=wx.ITEM_NORMAL,text='Load log')
1771        def OnLoadLog(event):
1772            # this appends. Perhaps we should ask to clear?
1773            import cPickle
1774            defnam = os.path.splitext(
1775                os.path.split(self.GSASprojectfile)[1]
1776                )[0]+'.gcmd'
1777            dlg = wx.FileDialog(self,
1778                'Choose an file to read saved actions', '.', defnam, 
1779                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1780                wx.OPEN|wx.CHANGE_DIR)
1781            dlg.CenterOnParent()
1782            try:
1783                if dlg.ShowModal() == wx.ID_OK:
1784                    filename = dlg.GetPath()
1785                    # make sure extension is correct
1786                    filename = os.path.splitext(filename)[0]+'.gcmd'
1787                else:
1788                    filename = None
1789            finally:
1790                dlg.Destroy()
1791            if filename and os.path.exists(filename):
1792                fp = open(filename,'rb')
1793                lines = fp.readline()
1794                for i in range(int(lines)):
1795                    log.G2logList.append(cPickle.load(fp))
1796                fp.close()
1797        self.Bind(wx.EVT_MENU, OnLoadLog, item)
1798
1799        item = menu.Append(
1800            help='Replay saved commands', id=wx.ID_ANY,
1801            kind=wx.ITEM_NORMAL,text='Replay log')
1802        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
1803
1804    def _init_Exports(self,menu):
1805        '''Find exporter routines and add them into menus
1806        '''
1807        # set up the top-level menus
1808        projectmenu = wx.Menu()
1809        item = menu.AppendMenu(
1810            wx.ID_ANY, 'Entire project as',
1811            projectmenu, help='Export entire project')
1812
1813        phasemenu = wx.Menu()
1814        item = menu.AppendMenu(
1815            wx.ID_ANY, 'Phase as',
1816            phasemenu, help='Export phase or sometimes phases')
1817
1818        powdermenu = wx.Menu()
1819        item = menu.AppendMenu(
1820            wx.ID_ANY, 'Powder data as',
1821            powdermenu, help='Export powder diffraction histogram(s)')
1822
1823        singlemenu = wx.Menu()
1824        item = menu.AppendMenu(
1825            wx.ID_ANY, 'Single crystal data as',
1826            singlemenu, help='Export single crystal histogram(s)')
1827
1828        imagemenu = wx.Menu()
1829        item = menu.AppendMenu(
1830            wx.ID_ANY, 'Image data as',
1831            imagemenu, help='Export powder image(s) data')
1832
1833        mapmenu = wx.Menu()
1834        item = menu.AppendMenu(
1835            wx.ID_ANY, 'Maps as',
1836            mapmenu, help='Export density map(s)')
1837
1838        # pdfmenu = wx.Menu()
1839        # item = menu.AppendMenu(
1840        #     wx.ID_ANY, 'PDFs as',
1841        #     pdfmenu, help='Export pair distribution function(s)')
1842
1843        # find all the exporter files
1844        pathlist = sys.path
1845        filelist = []
1846        for path in pathlist:
1847            for filename in glob.iglob(os.path.join(path,"G2export*.py")):
1848                filelist.append(filename)   
1849        filelist = sorted(list(set(filelist))) # remove duplicates
1850        exporterlist = []
1851        # go through the routines and import them, saving objects that
1852        # have export routines (method Exporter)
1853        for filename in filelist:
1854            path,rootname = os.path.split(filename)
1855            pkg = os.path.splitext(rootname)[0]
1856            try:
1857                fp = None
1858                fp, fppath,desc = imp.find_module(pkg,[path,])
1859                pkg = imp.load_module(pkg,fp,fppath,desc)
1860                for clss in inspect.getmembers(pkg): # find classes defined in package
1861                    if clss[0].startswith('_'): continue
1862                    if inspect.isclass(clss[1]):
1863                        # check if we have the required methods
1864                        for m in 'Exporter','loadParmDict':
1865                            if not hasattr(clss[1],m): break
1866                            if not callable(getattr(clss[1],m)): break
1867                        else:
1868                            exporter = clss[1](self) # create an export instance
1869                            exporterlist.append(exporter)
1870            except AttributeError:
1871                print 'Import_'+errprefix+': Attribute Error'+str(filename)
1872                pass
1873            except ImportError:
1874                print 'Import_'+errprefix+': Error importing file'+str(filename)
1875                pass
1876            if fp: fp.close()
1877        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
1878        for obj in exporterlist:
1879            #print 'exporter',obj
1880            for typ in obj.exporttype:
1881                if typ == "project":
1882                    submenu = projectmenu
1883                elif typ == "phase":
1884                    submenu = phasemenu
1885                elif typ == "powder":
1886                    submenu = powdermenu
1887                elif typ == "single":
1888                    submenu = singlemenu
1889                elif typ == "image":
1890                    submenu = imagemenu
1891                elif typ == "map":
1892                    submenu = mapmenu
1893                # elif typ == "pdf":
1894                #     submenu = pdfmenu
1895                else:
1896                    print("Error, unknown type in "+str(obj))
1897                    break
1898                item = submenu.Append(
1899                    wx.ID_ANY,
1900                    help=obj.longFormatName,
1901                    kind=wx.ITEM_NORMAL,
1902                    text=obj.formatName)
1903                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
1904                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
1905               
1906        #code to debug an Exporter. hard-coded the routine below, to allow a reload before use
1907        # def DebugExport(event):
1908        #      print 'start reload'
1909        #      reload(G2IO)
1910        #      import G2export_pwdr as dev
1911        #      reload(dev)
1912        #      dev.ExportPowderFXYE(self).Exporter(event)
1913        # item = menu.Append(
1914        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1915        #     help="debug exporter",text="test Export FXYE")
1916        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
1917        # # #self.ExportLookup[item.GetId()] = 'image'
1918        # self.ExportLookup[item.GetId()] = 'powder'
1919
1920    def _Add_ExportMenuItems(self,parent):
1921        # item = parent.Append(
1922        #     help='Select PWDR item to enable',id=wx.ID_ANY,
1923        #     kind=wx.ITEM_NORMAL,
1924        #     text='Export Powder Patterns...')
1925        # self.ExportPattern.append(item)
1926        # item.Enable(False)
1927        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1928
1929        item = parent.Append(
1930            help='',id=wx.ID_ANY,
1931            kind=wx.ITEM_NORMAL,
1932            text='Export All Peak Lists...')
1933        self.ExportPeakList.append(item)
1934        item.Enable(True)
1935        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1936
1937        item = parent.Append(
1938            help='',id=wx.ID_ANY,
1939            kind=wx.ITEM_NORMAL,
1940            text='Export HKLs...')
1941        self.ExportHKL.append(item)
1942        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1943
1944        item = parent.Append(
1945            help='Select PDF item to enable',
1946            id=wx.ID_ANY,
1947            kind=wx.ITEM_NORMAL,
1948            text='Export PDF...')
1949        self.ExportPDF.append(item)
1950        item.Enable(False)
1951        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1952
1953    def FillMainMenu(self,menubar):
1954        '''Define contents of the main GSAS-II menu for the (main) data tree window.
1955        For the mac, this is also called for the data item windows as well so that
1956        the main menu items are data menu as well.
1957        '''
1958        File = wx.Menu(title='')
1959        menubar.Append(menu=File, title='&File')
1960        self._Add_FileMenuItems(File)
1961        Data = wx.Menu(title='')
1962        menubar.Append(menu=Data, title='Data')
1963        self._Add_DataMenuItems(Data)
1964        Calculate = wx.Menu(title='')       
1965        menubar.Append(menu=Calculate, title='&Calculate')
1966        self._Add_CalculateMenuItems(Calculate)
1967        Import = wx.Menu(title='')       
1968        menubar.Append(menu=Import, title='Import')
1969        self._Add_ImportMenu_Phase(Import)
1970        self._Add_ImportMenu_powder(Import)
1971        self._Add_ImportMenu_Sfact(Import)
1972        self._Add_ImportMenu_smallangle(Import)
1973        #======================================================================
1974        # Code to help develop/debug an importer, much is hard-coded below
1975        # but module is reloaded before each use, allowing faster testing
1976        # def DebugImport(event):
1977        #     print 'start reload'
1978        #     import G2phase_ISO as dev
1979        #     reload(dev)
1980        #     rd = dev.ISODISTORTPhaseReader()
1981        #     self.ImportMenuId[event.GetId()] = rd
1982        #     self.OnImportPhase(event)
1983            # or ----------------------------------------------------------------------
1984            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
1985            # special debug code
1986            # or ----------------------------------------------------------------------
1987            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
1988            # fp = open(filename,'Ur')
1989            # if not rd.ContentsValidator(fp):
1990            #     print 'not validated'
1991            #     # make a list of used phase ranId's
1992            # phaseRIdList = []
1993            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1994            # if sub:
1995            #     item, cookie = self.PatternTree.GetFirstChild(sub)
1996            #     while item:
1997            #         phaseName = self.PatternTree.GetItemText(item)
1998            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
1999            #         if ranId: phaseRIdList.append(ranId)
2000            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2001            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2002            #     print 'read OK'
2003        # item = Import.Append(
2004        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2005        #     help="debug importer",text="test importer")
2006        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2007        #======================================================================
2008        self.ExportMenu = wx.Menu(title='')
2009        menubar.Append(menu=self.ExportMenu, title='Export')
2010        self._init_Exports(self.ExportMenu)
2011        self._Add_ExportMenuItems(self.ExportMenu)
2012        if GSASIIpath.GetConfigValue('Enable_logging'):
2013            self.MacroMenu = wx.Menu(title='')
2014            menubar.Append(menu=self.MacroMenu, title='Macro')
2015            self._init_Macro()
2016        HelpMenu=G2G.MyHelp(self,helpType='Data tree',
2017            morehelpitems=[('&Tutorials','Tutorials'),
2018                           ('&New Tutorials','NewTutorials'), # this will eventually go away
2019                           ])
2020        menubar.Append(menu=HelpMenu,title='&Help')
2021
2022    def _init_ctrls(self, parent):
2023        wx.Frame.__init__(self, name='GSASII', parent=parent,
2024            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2025        clientSize = wx.ClientDisplayRect()
2026        Size = self.GetSize()
2027        xPos = clientSize[2]-Size[0]
2028        self.SetPosition(wx.Point(xPos,clientSize[1]))
2029        self._init_Imports()
2030        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2031        self.MakePDF = []
2032        self.Refine = []
2033        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2034        #self.ExportPattern = []
2035        self.ExportPeakList = []
2036        self.ExportHKL = []
2037        self.ExportPDF = []
2038        self.ExportPhase = []
2039        self.ExportCIF = []
2040        #
2041        self.GSASIIMenu = wx.MenuBar()
2042        # create a list of all dataframe menus (appended in PrefillDataMenu)
2043        self.dataMenuBars = [self.GSASIIMenu]
2044        self.MacroStatusList = []
2045        self.FillMainMenu(self.GSASIIMenu)
2046        self.SetMenuBar(self.GSASIIMenu)
2047        self.Bind(wx.EVT_SIZE, self.OnSize)
2048        self.Status = self.CreateStatusBar()
2049        self.mainPanel = wx.Panel(self,-1)
2050       
2051        wxID_PATTERNTREE = wx.NewId()
2052        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2053        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2054            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2055        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnPatternTreeSelChanged)
2056        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2057            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2058        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2059            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2060        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2061            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2062        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2063            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2064        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2065            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2066        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2067            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2068        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2069        self.root = self.PatternTree.root
2070        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2071            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2072        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
2073        plotFrame.Show()
2074       
2075        self.dataDisplay = None
2076       
2077    def __init__(self, parent):
2078        self.ExportLookup = {}
2079        self._init_ctrls(parent)
2080        self.Image = wx.Image(
2081            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2082            wx.BITMAP_TYPE_ICO)
2083        if "wxMSW" in wx.PlatformInfo:
2084            img = self.Image.Scale(16, 16).ConvertToBitmap()
2085        elif "wxGTK" in wx.PlatformInfo:
2086            img = self.Image.Scale(22, 22).ConvertToBitmap()
2087        else:
2088            img = self.Image.ConvertToBitmap()
2089        self.SetIcon(wx.IconFromBitmap(img))
2090        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2091        # various defaults
2092        self.oldFocus = None
2093        self.GSASprojectfile = ''
2094        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
2095        self.exportDir = None  # the last directory used for exports, if any.
2096        self.undofile = ''
2097        self.TreeItemDelete = False
2098#        self.Offset = [0.0,0.0]
2099#        self.delOffset = .02
2100#        self.refOffset = -1.0
2101#        self.refDelt = .01
2102        self.Weight = False
2103        self.IfPlot = False
2104        self.DDShowAll = False
2105        self.PatternId = 0
2106        self.PickId = 0
2107        self.PeakTable = []
2108        self.LimitsTable = []
2109        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2110        self.HKL = []
2111        self.Lines = []
2112        self.itemPicked = None
2113        self.dataFrame = None
2114        self.Interpolate = 'nearest'
2115        self.ContourColor = 'Paired'
2116        self.VcovColor = 'RdYlGn'
2117        self.RamaColor = 'Blues'
2118        self.Projection = 'equal area'
2119        self.logPlot = False
2120        self.sqPlot = False
2121        self.ErrorBars = False
2122        self.Contour = False
2123        self.Legend = False
2124        self.SinglePlot = True
2125        self.SubBack = False
2126        self.seqReverse = False
2127        self.plotView = 0
2128        self.Image = 0
2129        self.oldImagefile = ''
2130        self.ImageZ = []
2131        self.Integrate = 0
2132        self.imageDefault = {}
2133        self.Sngl = False
2134        self.ifGetRing = False
2135        self.MaskKey = ''           #trigger for making image masks
2136        self.StrainKey = ''         #ditto for new strain d-zeros
2137        self.EnablePlot = True
2138        self.Tutorials = False      #used for changing default directory
2139        arg = sys.argv
2140        if len(arg) > 1 and arg[1]:
2141            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2142            self.dirname = os.path.dirname(arg[1])
2143            if self.dirname: os.chdir(self.dirname)
2144            try:
2145                G2IO.ProjFileOpen(self)
2146                self.PatternTree.Expand(self.root)
2147                for item in self.Refine: item.Enable(True)
2148                self.EnableSeqRefineMenu()
2149            except:
2150                print 'Error opening file',arg[1]
2151
2152    def OnSize(self,event):
2153        'Called when the main window is resized. Not sure why'
2154        w,h = self.GetClientSizeTuple()
2155        self.mainPanel.SetSize(wx.Size(w,h))
2156        self.PatternTree.SetSize(wx.Size(w,h))
2157                       
2158    def OnPatternTreeSelChanged(self, event):
2159        '''Called when a data tree item is selected'''
2160        if self.TreeItemDelete:
2161            self.TreeItemDelete = False
2162        else:
2163            pltNum = self.G2plotNB.nb.GetSelection()
2164            if pltNum >= 0:                         #to avoid the startup with no plot!
2165                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2166                pltPlot = pltPage.figure
2167            item = event.GetItem()
2168            G2gd.MovePatternTreeToGrid(self,item)
2169            if self.oldFocus:
2170                self.oldFocus.SetFocus()
2171       
2172    def OnPatternTreeItemCollapsed(self, event):
2173        'Called when a tree item is collapsed'
2174        event.Skip()
2175
2176    def OnPatternTreeItemExpanded(self, event):
2177        'Called when a tree item is expanded'
2178        event.Skip()
2179       
2180    def OnPatternTreeItemDelete(self, event):
2181        'Called when a tree item is deleted -- not sure what this does'
2182        self.TreeItemDelete = True
2183
2184    def OnPatternTreeItemActivated(self, event):
2185        'Called when a tree item is activated'
2186        event.Skip()
2187       
2188    def OnPatternTreeBeginRDrag(self,event):
2189        # testing this - doesn't work! Binds commented out above
2190        event.Allow()
2191        self.BeginDragId = event.GetItem()
2192        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2193        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2194        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2195        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2196        while item:     #G2 data tree has no sub children under a child of a tree item
2197            name = self.PatternTree.GetItemText(item)
2198            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2199            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2200       
2201    def OnPatternTreeEndDrag(self,event):
2202        # testing this - doesn't work! Binds commented out above
2203        event.Allow()
2204        self.EndDragId = event.GetItem()
2205        try:
2206            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2207        except:
2208            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2209            NewParent = self.root
2210        if self.ParentId != NewParent:
2211            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2212        else:
2213            Name,Item = self.DragData[0]
2214            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2215            self.PatternTree.SetItemPyData(NewId,Item)
2216            for name,item in self.DragData[1:]:     #loop over children
2217                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2218                self.PatternTree.SetItemPyData(Id,item)
2219            self.PatternTree.Delete(self.BeginDragId)
2220            G2gd.MovePatternTreeToGrid(self,NewId)
2221       
2222    def OnPatternTreeKeyDown(self,event):
2223        'Allows stepping through the tree with the up/down arrow keys'
2224        key = event.GetKeyCode()
2225        item = self.PickId
2226        if type(item) is int: return # is this the toplevel in tree?
2227        if key == wx.WXK_UP:
2228            self.oldFocus = wx.Window.FindFocus()
2229            self.PatternTree.GetPrevSibling(item)
2230        elif key == wx.WXK_DOWN:
2231            self.oldFocus = wx.Window.FindFocus()
2232            self.PatternTree.GetNextSibling(item)
2233               
2234    def OnReadPowderPeaks(self,event):
2235        'Bound to menu Data/Read Powder Peaks -- still needed?'
2236        Cuka = 1.54052
2237        self.CheckNotebook()
2238        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
2239            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
2240        try:
2241            if dlg.ShowModal() == wx.ID_OK:
2242                self.HKL = []
2243                self.powderfile = dlg.GetPath()
2244                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
2245                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2246                data = ['PKS',Cuka,0.0]
2247                names = ['Type','Lam','Zero'] 
2248                codes = [0,0,0]
2249                inst = [G2IO.makeInstDict(names,data,codes),{}]
2250                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2251                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2252                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2253                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2254                self.PatternTree.Expand(Id)
2255                self.PatternTree.SelectItem(Id)
2256                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2257        finally:
2258            dlg.Destroy()
2259                       
2260    def OnImageRead(self,event):
2261        'Called to read in an image in any known format'
2262        self.CheckNotebook()
2263        dlg = wx.FileDialog(
2264            self, 'Choose image files', '.', '',
2265            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png)|'
2266            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png;*.zip|'
2267            'European detector file (*.edf)|*.edf|'
2268            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2269            'MAR file (*.mar*)|*.mar*|'
2270            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2271            'ADSC Image (*.img)|*.img|'
2272            'GSAS-II Image (*.G2img)|*.G2img|'
2273            'Portable Network Graphics image (*.png)|*.png|'
2274            'Zip archive (*.zip)|*.zip|'
2275            'All files (*.*)|*.*',
2276            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
2277        try:
2278            if dlg.ShowModal() == wx.ID_OK:
2279                imagefiles = dlg.GetPaths()
2280                imagefiles.sort()
2281                for imagefile in imagefiles:
2282                    # if a zip file, open and extract
2283                    if os.path.splitext(imagefile)[1].lower() == '.zip':
2284                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
2285                        if extractedfile is not None and extractedfile != imagefile:
2286                            imagefile = extractedfile
2287                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
2288                    if Comments:
2289                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
2290                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2291                        Imax = np.amax(Image)
2292                        Imin = max(0.0,np.amin(Image))          #force positive
2293                        if self.imageDefault:
2294                            Data = copy.copy(self.imageDefault)
2295                            Data['showLines'] = True
2296                            Data['ring'] = []
2297                            Data['rings'] = []
2298                            Data['cutoff'] = 10
2299                            Data['pixLimit'] = 20
2300                            Data['edgemin'] = 100000000
2301                            Data['calibdmin'] = 0.5
2302                            Data['calibskip'] = 0
2303                            Data['ellipses'] = []
2304                            Data['calibrant'] = ''
2305                            Data['GonioAngles'] = [0.,0.,0.]
2306                            Data['DetDepthRef'] = False
2307                        else:
2308                            Data['type'] = 'PWDR'
2309                            Data['color'] = 'Paired'
2310                            Data['tilt'] = 0.0
2311                            Data['rotation'] = 0.0
2312                            Data['showLines'] = False
2313                            Data['ring'] = []
2314                            Data['rings'] = []
2315                            Data['cutoff'] = 10
2316                            Data['pixLimit'] = 20
2317                            Data['calibdmin'] = 0.5
2318                            Data['calibskip'] = 0
2319                            Data['edgemin'] = 100000000
2320                            Data['ellipses'] = []
2321                            Data['GonioAngles'] = [0.,0.,0.]
2322                            Data['DetDepth'] = 0.
2323                            Data['DetDepthRef'] = False
2324                            Data['calibrant'] = ''
2325                            Data['IOtth'] = [2.0,5.0]
2326                            Data['LRazimuth'] = [135,225]
2327                            Data['azmthOff'] = 0.0
2328                            Data['outChannels'] = 2500
2329                            Data['outAzimuths'] = 1
2330                            Data['centerAzm'] = False
2331                            Data['fullIntegrate'] = False
2332                            Data['setRings'] = False
2333                            Data['background image'] = ['',-1.0]                           
2334                            Data['dark image'] = ['',-1.0]
2335                        Data['setDefault'] = False
2336                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2337                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
2338                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2339                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2340                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2341                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2342                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
2343                        self.PickId = Id
2344                        self.Image = Id
2345                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!               
2346                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
2347        finally:
2348            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2349            os.chdir(path)
2350            dlg.Destroy()
2351
2352    def CheckNotebook(self):
2353        '''Make sure the data tree has the minimally expected controls.
2354        (BHT) correct?
2355        '''
2356        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2357            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2358            self.PatternTree.SetItemPyData(sub,[''])
2359        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2360            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2361            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2362        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2363            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2364            self.PatternTree.SetItemPyData(sub,{})
2365        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2366            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2367            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2368        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2369            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2370            self.PatternTree.SetItemPyData(sub,{})
2371        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2372            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2373            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2374                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2375               
2376    class CopyDialog(wx.Dialog):
2377        '''Creates a dialog for copying control settings between
2378        data tree items'''
2379        def __init__(self,parent,title,text,data):
2380            wx.Dialog.__init__(self,parent,-1,title, 
2381                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2382            self.data = data
2383            panel = wx.Panel(self)
2384            mainSizer = wx.BoxSizer(wx.VERTICAL)
2385            topLabl = wx.StaticText(panel,-1,text)
2386            mainSizer.Add((10,10),1)
2387            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2388            mainSizer.Add((10,10),1)
2389            ncols = len(data)/40+1
2390            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2391            for id,item in enumerate(self.data):
2392                ckbox = wx.CheckBox(panel,id,item[1])
2393                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2394                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2395            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2396            OkBtn = wx.Button(panel,-1,"Ok")
2397            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2398            cancelBtn = wx.Button(panel,-1,"Cancel")
2399            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2400            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2401            btnSizer.Add((20,20),1)
2402            btnSizer.Add(OkBtn)
2403            btnSizer.Add((20,20),1)
2404            btnSizer.Add(cancelBtn)
2405            btnSizer.Add((20,20),1)
2406           
2407            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2408            panel.SetSizer(mainSizer)
2409            panel.Fit()
2410            self.Fit()
2411       
2412        def OnCopyChange(self,event):
2413            id = event.GetId()
2414            self.data[id][0] = self.FindWindowById(id).GetValue()       
2415           
2416        def OnOk(self,event):
2417            parent = self.GetParent()
2418            parent.Raise()
2419            self.EndModal(wx.ID_OK)             
2420           
2421        def OnCancel(self,event):
2422            parent = self.GetParent()
2423            parent.Raise()
2424            self.EndModal(wx.ID_CANCEL)             
2425           
2426        def GetData(self):
2427            return self.data
2428       
2429    class SumDialog(wx.Dialog):
2430        'Allows user to supply scale factor(s) when summing data'
2431        def __init__(self,parent,title,text,dataType,data):
2432            wx.Dialog.__init__(self,parent,-1,title, 
2433                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2434            self.data = data
2435            panel = wx.Panel(self)
2436            mainSizer = wx.BoxSizer(wx.VERTICAL)
2437            topLabl = wx.StaticText(panel,-1,text)
2438            mainSizer.Add((10,10),1)
2439            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2440            mainSizer.Add((10,10),1)
2441            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2442            for id,item in enumerate(self.data[:-1]):
2443                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2444                name.SetEditable(False)
2445                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2446                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2447                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2448                dataGridSizer.Add(scale,0,wx.LEFT,10)
2449                dataGridSizer.Add(name,0,wx.RIGHT,10)
2450            if dataType:
2451                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2452                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2453                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2454                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2455                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2456                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2457            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2458            OkBtn = wx.Button(panel,-1,"Ok")
2459            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2460            cancelBtn = wx.Button(panel,-1,"Cancel")
2461            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2462            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2463            btnSizer.Add((20,20),1)
2464            btnSizer.Add(OkBtn)
2465            btnSizer.Add((20,20),1)
2466            btnSizer.Add(cancelBtn)
2467            btnSizer.Add((20,20),1)
2468           
2469            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2470            panel.SetSizer(mainSizer)
2471            panel.Fit()
2472            self.Fit()
2473
2474        def OnScaleChange(self,event):
2475            id = event.GetId()
2476            value = self.FindWindowById(id).GetValue()
2477            try:
2478                self.data[id][0] = float(value)
2479                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2480            except ValueError:
2481                if value and '-' not in value[0]:
2482                    print 'bad input - numbers only'
2483                    self.FindWindowById(id).SetValue('0.000')
2484           
2485        def OnNameChange(self,event):
2486            self.data[-1] = self.name.GetValue() 
2487           
2488        def OnOk(self,event):
2489            parent = self.GetParent()
2490            parent.Raise()
2491            self.EndModal(wx.ID_OK)             
2492           
2493        def OnCancel(self,event):
2494            parent = self.GetParent()
2495            parent.Raise()
2496            self.EndModal(wx.ID_CANCEL)             
2497           
2498        def GetData(self):
2499            return self.data
2500                       
2501    def OnPwdrSum(self,event):
2502        'Sum together powder data(?)'
2503        TextList = []
2504        DataList = []
2505        SumList = []
2506        Names = []
2507        Inst = None
2508        SumItemList = []
2509        Comments = ['Sum equals: \n']
2510        if self.PatternTree.GetCount():
2511            item, cookie = self.PatternTree.GetFirstChild(self.root)
2512            while item:
2513                name = self.PatternTree.GetItemText(item)
2514                Names.append(name)
2515                if 'PWDR' in name:
2516                    TextList.append([0.0,name])
2517                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2518                    if not Inst:
2519                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2520                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2521            if len(TextList) < 2:
2522                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2523                return
2524            TextList.append('default_sum_name')               
2525            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2526            try:
2527                if dlg.ShowModal() == wx.ID_OK:
2528                    lenX = 0
2529                    Xminmax = [0,0]
2530                    Xsum = []
2531                    Ysum = []
2532                    Vsum = []
2533                    result = dlg.GetData()
2534                    for i,item in enumerate(result[:-1]):
2535                        scale,name = item
2536                        data = DataList[i]
2537                        if scale:
2538                            Comments.append("%10.3f %s" % (scale,' * '+name))
2539                            x,y,w,yc,yb,yd = data   #numpy arrays!
2540                            v = 1./w
2541                            if lenX:
2542                                if lenX != len(x):
2543                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2544                                        '\nExpected:'+str(lenX)+ \
2545                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2546                                    return
2547                            else:
2548                                lenX = len(x)
2549                            if Xminmax[1]:
2550                                if Xminmax != [x[0],x[-1]]:
2551                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2552                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2553                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2554                                    return
2555                                else:
2556                                    for j,yi in enumerate(y):
2557                                         Ysum[j] += scale*yi
2558                                         Vsum[j] += abs(scale)*v[j]
2559                            else:
2560                                Xminmax = [x[0],x[-1]]
2561                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2562                                for j,yi in enumerate(y):
2563                                    Xsum.append(x[j])
2564                                    Ysum.append(scale*yi)
2565                                    Vsum.append(abs(scale*v[j]))
2566                    Wsum = 1./np.array(Vsum)
2567                    outname = 'PWDR '+result[-1]
2568                    Id = 0
2569                    if outname in Names:
2570                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2571                        try:
2572                            if dlg2.ShowModal() == wx.ID_OK:
2573                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2574                                self.PatternTree.Delete(Id)
2575                        finally:
2576                            dlg2.Destroy()
2577                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2578                    if Id:
2579                        Sample = G2pdG.SetDefaultSample()
2580                        valuesdict = {
2581                            'wtFactor':1.0,
2582                            'Dummy':False,
2583                            'ranId':ran.randint(0,sys.maxint),
2584                            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
2585                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2586                            }
2587                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2588                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2589                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2590                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2591                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2592                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2593                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2594                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2595                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2596                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2597                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2598                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2599                        self.PatternTree.SelectItem(Id)
2600                        self.PatternTree.Expand(Id)
2601            finally:
2602                dlg.Destroy()
2603
2604    def OnImageSum(self,event):
2605        'Sum together image data(?)'
2606        TextList = []
2607        DataList = []
2608        SumList = []
2609        Names = []
2610        Inst = []
2611        SumItemList = []
2612        Comments = ['Sum equals: \n']
2613        if self.PatternTree.GetCount():
2614            item, cookie = self.PatternTree.GetFirstChild(self.root)
2615            while item:
2616                name = self.PatternTree.GetItemText(item)
2617                Names.append(name)
2618                if 'IMG' in name:
2619                    TextList.append([0.0,name])
2620                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2621                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2622                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2623            if len(TextList) < 2:
2624                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2625                return
2626            TextList.append('default_sum_name')               
2627            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2628            try:
2629                if dlg.ShowModal() == wx.ID_OK:
2630                    imSize = 0
2631                    result = dlg.GetData()
2632                    First = True
2633                    Found = False
2634                    for i,item in enumerate(result[:-1]):
2635                        scale,name = item
2636                        data = DataList[i]
2637                        if scale:
2638                            Found = True                               
2639                            Comments.append("%10.3f %s" % (scale,' * '+name))
2640                            Npix,imagefile = data
2641                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2642                            if First:
2643                                newImage = np.zeros_like(image)
2644                                First = False
2645                            if imSize:
2646                                if imSize != Npix:
2647                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2648                                        '\nExpected:'+str(imSize)+ \
2649                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2650                                    return
2651                                newImage = newImage+scale*image
2652                            else:
2653                                imSize = Npix
2654                                newImage = newImage+scale*image
2655                            del(image)
2656                    if not Found:
2657                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2658                        return
2659                       
2660                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2661                    outname = 'IMG '+result[-1]
2662                    Id = 0
2663                    if outname in Names:
2664                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2665                        try:
2666                            if dlg2.ShowModal() == wx.ID_OK:
2667                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2668                        finally:
2669                            dlg2.Destroy()
2670                    else:
2671                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2672                    if Id:
2673                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2674                            'G2img files (*.G2img)|*.G2img', 
2675                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2676                        if dlg.ShowModal() == wx.ID_OK:
2677                            newimagefile = dlg.GetPath()
2678                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2679                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2680                            Imax = np.amax(newImage)
2681                            Imin = np.amin(newImage)
2682                            newImage = []
2683                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2684                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2685                        del(newImage)
2686                        if self.imageDefault:
2687                            Data = copy.copy(self.imageDefault)
2688                        Data['showLines'] = True
2689                        Data['ring'] = []
2690                        Data['rings'] = []
2691                        Data['cutoff'] = 10
2692                        Data['pixLimit'] = 20
2693                        Data['ellipses'] = []
2694                        Data['calibrant'] = ''
2695                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2696                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2697                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2698                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2699                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2700                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2701                        self.PatternTree.SelectItem(Id)
2702                        self.PatternTree.Expand(Id)
2703                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2704                        self.Image = self.PickId
2705            finally:
2706                dlg.Destroy()
2707                     
2708    def OnAddPhase(self,event):
2709        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2710        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2711            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2712        else:
2713            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2714        PhaseName = ''
2715        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2716            style=wx.OK)
2717        if dlg.ShowModal() == wx.ID_OK:
2718            PhaseName = dlg.GetValue()
2719        dlg.Destroy()
2720        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2721        E,SGData = G2spc.SpcGroup('P 1')
2722        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2723       
2724    def OnDeletePhase(self,event):
2725        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2726        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2727        if self.dataFrame:
2728            self.dataFrame.Clear() 
2729        TextList = []
2730        DelList = []
2731        DelItemList = []
2732        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2733            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2734        else:
2735            return
2736        if sub:
2737            item, cookie = self.PatternTree.GetFirstChild(sub)
2738            while item:
2739                TextList.append(self.PatternTree.GetItemText(item))
2740                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2741            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2742            try:
2743                if dlg.ShowModal() == wx.ID_OK:
2744                    result = dlg.GetSelections()
2745                    for i in result: DelList.append([i,TextList[i]])
2746                    item, cookie = self.PatternTree.GetFirstChild(sub)
2747                    i = 0
2748                    while item:
2749                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2750                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2751                        i += 1
2752                    for item in DelItemList:
2753                        name = self.PatternTree.GetItemText(item)
2754                        self.PatternTree.Delete(item)
2755                        self.G2plotNB.Delete(name)
2756                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2757                    while item:
2758                        name = self.PatternTree.GetItemText(item)
2759                        if 'PWDR' in name:
2760                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2761                            refList = self.PatternTree.GetItemPyData(Id)
2762                            if len(refList):
2763                                for i,item in DelList:
2764                                    if item in refList:
2765                                        del(refList[item])
2766                            self.PatternTree.SetItemPyData(Id,refList)
2767                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2768            finally:
2769                dlg.Destroy()
2770               
2771    def OnRenameData(self,event):
2772        'Renames an existing phase. Called by Data/Rename Phase menu'
2773        name = self.PatternTree.GetItemText(self.PickId)     
2774        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2775            dataType = name[:name.index(' ')+1]                 #includes the ' '
2776            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2777                defaultValue=name[name.index(' ')+1:])
2778            try:
2779                if dlg.ShowModal() == wx.ID_OK:
2780                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2781            finally:
2782                dlg.Destroy()
2783       
2784    def GetFileList(self,fileType,skip=None):        #potentially useful?
2785        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2786        fileList = []
2787        Source = ''
2788        id, cookie = self.PatternTree.GetFirstChild(self.root)
2789        while id:
2790            name = self.PatternTree.GetItemText(id)
2791            if fileType in name:
2792                if id == skip:
2793                    Source = name
2794                else:
2795                    fileList.append([False,name,id])
2796            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2797        if skip:
2798            return fileList,Source
2799        else:
2800            return fileList
2801           
2802    def OnDataDelete(self, event):
2803        '''Delete one or more histograms from data tree. Called by the
2804        Data/DeleteData menu
2805        '''
2806        TextList = ['All Data']
2807        DelList = []
2808        DelItemList = []
2809        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
2810        ifPWDR = False
2811        ifSASD = False
2812        ifIMG = False
2813        ifHKLF = False
2814        ifPDF = False
2815        if self.PatternTree.GetCount():
2816            item, cookie = self.PatternTree.GetFirstChild(self.root)
2817            while item:
2818                name = self.PatternTree.GetItemText(item)
2819                if name not in ['Notebook','Controls','Covariance','Constraints',
2820                    'Restraints','Phases','Rigid bodies']:
2821                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
2822                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
2823                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
2824                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
2825                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
2826                    TextList.append(name)
2827                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2828            if ifPWDR: TextList.insert(1,'All PWDR')
2829            if ifSASD: TextList.insert(1,'All SASD')
2830            if ifIMG: TextList.insert(1,'All IMG')
2831            if ifHKLF: TextList.insert(1,'All HKLF')
2832            if ifPDF: TextList.insert(1,'All PDF')               
2833            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2834            try:
2835                if dlg.ShowModal() == wx.ID_OK:
2836                    result = dlg.GetSelections()
2837                    for i in result: DelList.append(TextList[i])
2838                    if 'All Data' in DelList:
2839                        DelList = [item for item in TextList if item[:3] != 'All']
2840                    elif 'All PWDR' in DelList:
2841                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2842                    elif 'All SASD' in DelList:
2843                        DelList = [item for item in TextList if item[:4] == 'SASD']
2844                    elif 'All IMG' in DelList:
2845                        DelList = [item for item in TextList if item[:3] == 'IMG']
2846                    elif 'All HKLF' in DelList:
2847                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2848                    elif 'All PDF' in DelList:
2849                        DelList = [item for item in TextList if item[:3] == 'PDF']
2850                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2851                    while item:
2852                        itemName = self.PatternTree.GetItemText(item)
2853                        if itemName in DelList:
2854                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
2855                            elif 'SASD' in itemName: nItems['SASD'] -= 1
2856                            elif 'IMG' in itemName: nItems['IMG'] -= 1
2857                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
2858                            elif 'PDF' in itemName: nItems['PDF'] -= 1
2859                            DelItemList.append(item)
2860                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2861                    for item in DelItemList:
2862                        self.PatternTree.Delete(item)
2863                    self.PickId = 0
2864                    self.PatternId = 0
2865                    if nItems['PWDR']:
2866                        wx.CallAfter(G2plt.PlotPatterns,self,True)
2867                    else:
2868                        self.G2plotNB.Delete('Powder Patterns')
2869                    if not nItems['IMG']:
2870                        self.G2plotNB.Delete('2D Powder Image')
2871                    if not nItems['HKLF']:
2872                        self.G2plotNB.Delete('Structure Factors')
2873                        if '3D Structure Factors' in self.G2plotNB.plotList:
2874                            self.G2plotNB.Delete('3D Structure Factors')
2875            finally:
2876                dlg.Destroy()
2877
2878    def OnFileOpen(self, event, filename=None):
2879        '''Reads in a GSAS-II .gpx project file in response to the
2880        File/Open Project menu button
2881        '''
2882        result = wx.ID_OK
2883        Id = 0
2884        self.EnablePlot = False
2885        if self.PatternTree.GetChildrenCount(self.root,False):
2886            if self.dataFrame:
2887                self.dataFrame.Clear() 
2888            dlg = wx.MessageDialog(
2889                self,
2890                'Do you want to overwrite the current project? '
2891                'Any unsaved changes will be lost. Press OK to continue.',
2892                'Overwrite?',  wx.OK | wx.CANCEL)
2893            try:
2894                result = dlg.ShowModal()
2895                if result == wx.ID_OK:
2896                    self.PatternTree.DeleteChildren(self.root)
2897                    self.GSASprojectfile = ''
2898                    if self.HKL: self.HKL = []
2899                    if self.G2plotNB.plotList:
2900                        self.G2plotNB.clear()
2901            finally:
2902                dlg.Destroy()
2903        if result != wx.ID_OK: return
2904
2905        if not filename:
2906            if self.dataDisplay: self.dataDisplay.Destroy()
2907            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2908                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2909            try:
2910                if dlg.ShowModal() != wx.ID_OK: return
2911                self.GSASprojectfile = dlg.GetPath()
2912                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2913                self.dirname = dlg.GetDirectory()
2914            finally:
2915                dlg.Destroy()
2916        else:
2917            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2918            self.dirname = os.path.split(filename)[0]
2919
2920        G2IO.ProjFileOpen(self)
2921        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2922        self.PatternTree.Expand(self.root)
2923        self.HKL = []
2924        item, cookie = self.PatternTree.GetFirstChild(self.root)
2925        while item and not Id:
2926            name = self.PatternTree.GetItemText(item)
2927            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
2928                Id = item
2929            elif name == 'Controls':
2930                data = self.PatternTree.GetItemPyData(item)
2931                if data:
2932                    for item in self.Refine: item.Enable(True)
2933                    self.EnableSeqRefineMenu()
2934            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2935        if Id:
2936            self.EnablePlot = True
2937            self.PatternTree.SelectItem(Id)
2938        self.CheckNotebook()
2939        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2940
2941    def OnFileClose(self, event):
2942        '''Clears the data tree in response to the
2943        File/New Project menu button. User is given option to save
2944        the project.
2945        '''
2946        if self.dataFrame:
2947            self.dataFrame.Clear()
2948            self.dataFrame.SetLabel('GSAS-II data display') 
2949        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2950        try:
2951            result = dlg.ShowModal()
2952            if result == wx.ID_OK:
2953                self.OnFileSaveMenu(event)
2954            if result != wx.ID_CANCEL:
2955                self.GSASprojectfile = ''
2956                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2957                self.PatternTree.DeleteChildren(self.root)
2958                if self.HKL: self.HKL = []
2959                if self.G2plotNB.plotList:
2960                    self.G2plotNB.clear()
2961        finally:
2962            dlg.Destroy()
2963
2964    def OnFileSave(self, event):
2965        '''Save the current project in response to the
2966        File/Save Project menu button
2967        '''
2968       
2969        if self.GSASprojectfile: 
2970            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2971            G2IO.ProjFileSave(self)
2972        else:
2973            self.OnFileSaveas(event)
2974
2975    def OnFileSaveas(self, event):
2976        '''Save the current project in response to the
2977        File/Save as menu button
2978        '''
2979        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2980            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2981        try:
2982            if dlg.ShowModal() == wx.ID_OK:
2983                self.GSASprojectfile = dlg.GetPath()
2984                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2985                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2986                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2987                G2IO.ProjFileSave(self)
2988                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2989        finally:
2990            dlg.Destroy()
2991
2992    def ExitMain(self, event):
2993        '''Called if the main window is closed'''
2994        if self.undofile:
2995            os.remove(self.undofile)
2996        sys.exit()
2997       
2998    def OnFileExit(self, event):
2999        '''Called in response to the File/Quit menu button'''
3000        if self.dataFrame:
3001            self.dataFrame.Clear() 
3002            self.dataFrame.Destroy()
3003        self.Close()
3004       
3005    # def OnExportPatterns(self,event):
3006    #     names = ['All']
3007    #     exports = []
3008    #     item, cookie = self.PatternTree.GetFirstChild(self.root)
3009    #     while item:
3010    #         name = self.PatternTree.GetItemText(item)
3011    #         if 'PWDR' in name:
3012    #             names.append(name)
3013    #         item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3014    #     if names:
3015    #         dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
3016    #         if dlg.ShowModal() == wx.ID_OK:
3017    #             sel = dlg.GetSelections()
3018    #             if sel[0] == 0:
3019    #                 exports = names[1:]
3020    #             else:
3021    #                 for x in sel:
3022    #                     exports.append(names[x])
3023    #         dlg.Destroy()
3024    #     if exports:
3025    #         dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '',
3026    #             'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
3027    #             wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3028    #         try:
3029    #             if dlg.ShowModal() == wx.ID_OK:
3030    #                 powderfile = dlg.GetPath()
3031    #                 powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
3032    #                 if 'fxye' in powderfile:
3033    #                     G2IO.powderFxyeSave(self,exports,powderfile)
3034    #                 else:       #just xye
3035    #                     G2IO.powderXyeSave(self,exports,powderfile)
3036    #         finally:
3037    #             dlg.Destroy()
3038       
3039    def OnExportPeakList(self,event):
3040        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
3041            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3042        try:
3043            if dlg.ShowModal() == wx.ID_OK:
3044                self.peaklistfile = dlg.GetPath()
3045                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3046                file = open(self.peaklistfile,'w')               
3047                item, cookie = self.PatternTree.GetFirstChild(self.root)
3048                while item:
3049                    name = self.PatternTree.GetItemText(item)
3050                    if 'PWDR' in name:
3051                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3052                        while item2:
3053                            name2 = self.PatternTree.GetItemText(item2)
3054                            if name2 == 'Peak List':
3055                                peaks = self.PatternTree.GetItemPyData(item2)['peaks']
3056                                file.write("%s \n" % (name+' Peak List'))
3057                                if len(peaks[0]) == 8:
3058                                    file.write('%10s %12s %10s %10s\n'%('pos','int','sig','gam'))
3059                                else:
3060                                    file.write('%10s %12s %10s %10s %10s %10s\n'%('pos','int','alp','bet','sig','gam'))                                   
3061                                for peak in peaks:
3062                                    if len(peak) == 8:  #CW
3063                                        file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
3064                                            (peak[0],peak[2],peak[4],peak[6]))
3065                                    else:               #TOF - more cols
3066                                        file.write("%10.5f %12.2f %10.3f %10.3f %10.3f %10.3f\n" % \
3067                                            (peak[0],peak[2],peak[4],peak[6],peak[8],peak[10]))
3068                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3069                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3070                file.close()
3071        finally:
3072            dlg.Destroy()
3073       
3074    def OnExportHKL(self,event):
3075        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
3076            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3077        try:
3078            if dlg.ShowModal() == wx.ID_OK:
3079                self.peaklistfile = dlg.GetPath()
3080                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3081                file = open(self.peaklistfile,'w')               
3082                item, cookie = self.PatternTree.GetFirstChild(self.root)
3083                while item:
3084                    name = self.PatternTree.GetItemText(item)
3085                    if 'PWDR' in name:
3086                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3087                        while item2:
3088                            name2 = self.PatternTree.GetItemText(item2)
3089                            if name2 == 'Reflection Lists':
3090                                data = self.PatternTree.GetItemPyData(item2)
3091                                phases = data.keys()
3092                                for phase in phases:
3093                                    peaks = data[phase]
3094                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3095                                    if 'T' in peaks.get('Type','PXC'):
3096                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3097                                    else:               
3098                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3099                                    for peak in peaks['RefList']:
3100                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
3101                                        if 'T' in peaks.get('Type','PXC'):
3102                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %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                                        else:
3105                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3106                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3107                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3108                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3109                file.close()
3110        finally:
3111            dlg.Destroy()
3112       
3113    def OnExportPDF(self,event):
3114        #need S(Q) and G(R) to be saved here - probably best from selection?
3115        names = ['All']
3116        exports = []
3117        item, cookie = self.PatternTree.GetFirstChild(self.root)
3118        while item:
3119            name = self.PatternTree.GetItemText(item)
3120            if 'PDF' in name:
3121                names.append(name)
3122            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3123        if names:
3124            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3125            if dlg.ShowModal() == wx.ID_OK:
3126                sel = dlg.GetSelections()
3127                if sel[0] == 0:
3128                    exports = names[1:]
3129                else:
3130                    for x in sel:
3131                        exports.append(names[x])
3132            dlg.Destroy()
3133        if exports:
3134            G2IO.PDFSave(self,exports)
3135       
3136    def OnMakePDFs(self,event):
3137        '''Calculates PDFs
3138        '''
3139        sind = lambda x: math.sin(x*math.pi/180.)
3140        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3141        TextList = ['All PWDR']
3142        PDFlist = []
3143        Names = []
3144        if self.PatternTree.GetCount():
3145            id, cookie = self.PatternTree.GetFirstChild(self.root)
3146            while id:
3147                name = self.PatternTree.GetItemText(id)
3148                Names.append(name)
3149                if 'PWDR' in name:
3150                    TextList.append(name)
3151                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3152            if len(TextList) == 1:
3153                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3154                return
3155            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3156            try:
3157                if dlg.ShowModal() == wx.ID_OK:
3158                    result = dlg.GetSelections()
3159                    for i in result: PDFlist.append(TextList[i])
3160                    if 0 in result:
3161                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3162                    for item in PDFlist:
3163                        PWDRname = item[4:]
3164                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3165                        Data = {
3166                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3167                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3168                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3169                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3170                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3171                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3172                            'Lorch':True,}
3173                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3174                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3175                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3176                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3177                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3178                for item in self.ExportPDF: item.Enable(True)
3179            finally:
3180                dlg.Destroy()
3181               
3182    def GetPWDRdatafromTree(self,PWDRname):
3183        ''' Returns powder data from GSASII tree
3184
3185        :param str PWDRname: a powder histogram name as obtained from
3186          :meth:`GSASIIstruct.GetHistogramNames`
3187
3188        :returns: PWDRdata = powder data dictionary with
3189          Powder data arrays, Limits, Instrument Parameters,
3190          Sample Parameters           
3191        '''
3192        PWDRdata = {}
3193        try:
3194            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3195        except ValueError:
3196            PWDRdata['wtFactor'] = 1.0
3197        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3198        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3199        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3200        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3201        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3202        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3203        if 'ranId' not in PWDRdata:  # patch, add a random Id
3204            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3205        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3206            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3207        return PWDRdata
3208
3209    def GetHKLFdatafromTree(self,HKLFname):
3210        ''' Returns single crystal data from GSASII tree
3211
3212        :param str HKLFname: a single crystal histogram name as obtained
3213          from
3214          :meth:`GSASIIstruct.GetHistogramNames`
3215
3216        :returns: HKLFdata = single crystal data list of reflections
3217
3218        '''
3219        HKLFdata = {}
3220        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3221#        try:
3222#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3223#        except ValueError:
3224#            HKLFdata['wtFactor'] = 1.0
3225        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3226        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3227        return HKLFdata
3228       
3229    def GetPhaseData(self):
3230        '''Returns a dict with defined phases.
3231        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3232        get same info from GPX file.
3233        '''
3234        phaseData = {}
3235        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3236            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3237        else:
3238            print 'no phases found in GetPhaseData'
3239            sub = None
3240        if sub:
3241            item, cookie = self.PatternTree.GetFirstChild(sub)
3242            while item:
3243                phaseName = self.PatternTree.GetItemText(item)
3244                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3245                if 'ranId' not in phaseData[phaseName]:
3246                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3247                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3248        return phaseData
3249
3250    def GetPhaseInfofromTree(self):
3251        '''Get the phase names and their rId values,
3252        also the histograms used in each phase.
3253
3254        :returns: (phaseRIdList, usedHistograms) where
3255
3256          * phaseRIdList is a list of random Id values for each phase
3257          * usedHistograms is a dict where the keys are the phase names
3258            and the values for each key are a list of the histogram names
3259            used in each phase.
3260        '''
3261        phaseRIdList = []
3262        usedHistograms = {}
3263        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3264        if sub:
3265            item, cookie = self.PatternTree.GetFirstChild(sub)
3266            while item:
3267                phaseName = self.PatternTree.GetItemText(item)
3268                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3269                if ranId: phaseRIdList.append(ranId)
3270                data = self.PatternTree.GetItemPyData(item)
3271                UseList = data['Histograms']
3272                usedHistograms[phaseName] = UseList.keys()
3273                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3274        return phaseRIdList,usedHistograms
3275
3276    def GetPhaseNames(self):
3277        '''Returns a list of defined phases.
3278        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3279        get same info from GPX file.
3280        '''
3281        phaseNames = []
3282        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3283            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3284        else:
3285            print 'no phases found in GetPhaseNames'
3286            sub = None
3287        if sub:
3288            item, cookie = self.PatternTree.GetFirstChild(sub)
3289            while item:
3290                phase = self.PatternTree.GetItemText(item)
3291                phaseNames.append(phase)
3292                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3293        return phaseNames
3294   
3295    def GetHistogramNames(self,hType):
3296        """ Returns a list of histogram names found in the GSASII data tree
3297        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3298        get same info from GPX file.
3299       
3300        :param str hType: list of histogram types
3301        :return: list of histogram names
3302       
3303        """
3304        HistogramNames = []
3305        if self.PatternTree.GetCount():
3306            item, cookie = self.PatternTree.GetFirstChild(self.root)
3307            while item:
3308                name = self.PatternTree.GetItemText(item)
3309                if name[:4] in hType:
3310                    HistogramNames.append(name)       
3311                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3312
3313        return HistogramNames
3314                   
3315    def GetUsedHistogramsAndPhasesfromTree(self):
3316        ''' Returns all histograms that are found in any phase
3317        and any phase that uses a histogram.
3318        This also assigns numbers to used phases and histograms by the
3319        order they appear in the file.
3320        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3321        get same info from GPX file.
3322
3323        :returns: (Histograms,Phases)
3324
3325            * Histograms = dictionary of histograms as {name:data,...}
3326            * Phases = dictionary of phases that use histograms
3327        '''
3328        Histograms = {}
3329        Phases = {}
3330        phaseNames = self.GetPhaseNames()
3331        phaseData = self.GetPhaseData()
3332        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3333
3334        for phase in phaseData:
3335            Phase = phaseData[phase]
3336            pId = phaseNames.index(phase)
3337            Phase['pId'] = pId
3338            if Phase['Histograms']:
3339                if phase not in Phases:
3340                    Phases[phase] = Phase
3341                for hist in Phase['Histograms']:
3342                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3343                        Phase['Histograms'][hist]['Use'] = True         
3344                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3345                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3346                        if item:
3347                            if 'PWDR' in hist[:4]: 
3348                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3349                            elif 'HKLF' in hist[:4]:
3350                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3351                            hId = histoList.index(hist)
3352                            Histograms[hist]['hId'] = hId
3353                        else: # would happen if a referenced histogram were renamed or deleted
3354                            print('For phase "'+str(phase)+
3355                                  '" unresolved reference to histogram "'+str(hist)+'"')
3356        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3357        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3358        return Histograms,Phases
3359       
3360    def MakeLSParmDict(self):
3361        '''Load all parameters used for computation from the tree into a
3362        dict of paired values [value, refine flag]. Note that this is
3363        different than the parmDict used in the refinement, which only has
3364        values.
3365
3366        Note that similar things are done in
3367        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3368        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3369        a GPX file).
3370
3371        :returns: (parmDict,varyList) where:
3372
3373         * parmDict is a dict with values and refinement flags
3374           for each parameter and
3375         * varyList is a list of variables (refined parameters).
3376        '''
3377        parmDict = {}
3378        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3379        for phase in Phases:
3380            if 'pId' not in Phases[phase]:
3381                self.ErrorDialog('View parameter error','You must run least squares at least once')
3382                raise Exception,'No pId for phase '+str(phase)
3383        rigidbodyDict = self.PatternTree.GetItemPyData(   
3384            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3385        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3386        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3387        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3388        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3389        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3390        varyList = rbVary+phaseVary+hapVary+histVary
3391        parmDict.update(rbDict)
3392        parmDict.update(phaseDict)
3393        parmDict.update(hapDict)
3394        parmDict.update(histDict)
3395        for parm in parmDict:
3396            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3397                'Omega','Chi','Phi','nDebye','nPeaks']:
3398                parmDict[parm] = [parmDict[parm],'-']
3399            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3400                parmDict[parm] = [parmDict[parm],'-']
3401            elif parm in varyList:
3402                parmDict[parm] = [parmDict[parm],'T']
3403            else:
3404                parmDict[parm] = [parmDict[parm],'F']
3405        # for i in parmDict: print i,'\t',parmDict[i]
3406        # fl = open('parmDict.dat','wb')
3407        # import cPickle
3408        # cPickle.dump(parmDict,fl,1)
3409        # fl.close()
3410        return parmDict,varyList
3411
3412    def ShowLSParms(self,event):
3413        '''Displays a window showing all parameters in the refinement.
3414        Called from the Calculate/View LS Parms menu.
3415        '''
3416        parmDict,varyList = self.MakeLSParmDict()
3417        parmValDict = {}
3418        for i in parmDict:
3419            parmValDict[i] = parmDict[i][0]
3420           
3421        reqVaryList = tuple(varyList) # save requested variables
3422        try:
3423            # process constraints
3424            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3425            Constraints = self.PatternTree.GetItemPyData(sub)
3426            constList = []
3427            for item in Constraints:
3428                if item.startswith('_'): continue
3429                constList += Constraints[item]
3430            G2mv.InitVars()
3431            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3432            groups,parmlist = G2mv.GroupConstraints(constrDict)
3433            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3434            G2mv.Map2Dict(parmValDict,varyList)
3435        except:
3436            pass
3437        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3438        dlg.ShowModal()
3439        dlg.Destroy()
3440       
3441    def OnRefine(self,event):
3442        '''Perform a refinement.
3443        Called from the Calculate/Refine menu.
3444        '''       
3445        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3446        if Id:
3447            dlg = wx.MessageDialog(
3448                self,
3449                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3450                'Remove sequential results?',wx.OK|wx.CANCEL)
3451            if dlg.ShowModal() == wx.ID_OK:
3452                self.PatternTree.Delete(Id)
3453                dlg.Destroy()
3454            else:
3455                dlg.Destroy()
3456                return
3457
3458        self.OnFileSave(event)
3459        # check that constraints are OK here
3460        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3461        if errmsg:
3462            print('Error in constraints:\n'+errmsg+
3463                  '\nRefinement not possible')
3464            self.ErrorDialog('Constraint Error',
3465                             'Error in constraints:\n'+errmsg+
3466                             '\nRefinement not possible')
3467            return
3468        if warnmsg:
3469            print('Conflict between refinment flag settings and constraints:\n'+
3470                  warnmsg+'\nRefinement not possible')
3471            self.ErrorDialog('Refinement Flag Error',
3472                             'Conflict between refinment flag settings and constraints:\n'+
3473                             warnmsg+
3474                             '\nRefinement not possible')
3475            return
3476        #works - but it'd be better if it could restore plots
3477        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3478            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3479            parent=self)
3480        Size = dlg.GetSize()
3481        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3482            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3483        dlg.CenterOnParent()
3484        Rw = 100.00
3485        try:
3486            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3487        finally:
3488            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3489            dlg.Destroy()
3490            wx.Yield()
3491        oldId =  self.PatternTree.GetSelection()        #retain current selection
3492        oldName = self.PatternTree.GetItemText(oldId)
3493        parentId = self.PatternTree.GetItemParent(oldId)
3494        parentName = ''
3495        if parentId:
3496            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3497        dlg2 = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3498        try:
3499            if dlg2.ShowModal() == wx.ID_OK:
3500                Id = 0
3501                self.PatternTree.DeleteChildren(self.root)
3502                if self.HKL: self.HKL = []
3503                if self.G2plotNB.plotList:
3504                    self.G2plotNB.clear()
3505                G2IO.ProjFileOpen(self)
3506                item, cookie = self.PatternTree.GetFirstChild(self.root)
3507                while item and not Id:
3508                    name = self.PatternTree.GetItemText(item)
3509                    if name[:4] in ['PWDR','HKLF']:
3510                        Id = item
3511                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3512                if Id:
3513                    self.PatternTree.SelectItem(Id)
3514                if parentName:
3515                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3516                    if parentId:
3517                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3518                    else:
3519                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3520                    self.PatternTree.SelectItem(itemId)
3521        finally:
3522            dlg2.Destroy()
3523
3524    def OnSeqRefine(self,event):
3525        '''Perform a sequential refinement.
3526        Called from the Calculate/Sequential refine menu.
3527        '''       
3528        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3529        if not Id:
3530            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3531            self.PatternTree.SetItemPyData(Id,{})           
3532        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3533        Controls['ShowCell'] = True
3534        self.OnFileSave(event)
3535        # check that constraints are OK here
3536        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3537        if errmsg:
3538            print('Error in constraints:\n'+errmsg+
3539                  '\nRefinement not possible')
3540            self.ErrorDialog('Constraint Error',
3541                             'Error in constraints:\n'+errmsg+
3542                             '\nRefinement not possible')
3543            return
3544        if warnmsg:
3545            print('Conflict between refinment flag settings and constraints:\n'+
3546                  warnmsg+'\nRefinement not possible')
3547            self.ErrorDialog('Refinement Flag Error',
3548                             'Conflict between refinment flag settings and constraints:\n'+
3549                             warnmsg+'\nRefinement not possible')
3550            return
3551        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3552            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3553            parent=self)           
3554        Size = dlg.GetSize()
3555        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3556            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3557        dlg.CenterOnParent()
3558        try:
3559            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3560        finally:
3561            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3562            dlg.Destroy()
3563            wx.Yield()
3564        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3565        try:
3566            if dlg.ShowModal() == wx.ID_OK:
3567                Id = 0
3568                self.PatternTree.DeleteChildren(self.root)
3569                if self.HKL: self.HKL = []
3570                if self.G2plotNB.plotList:
3571                    self.G2plotNB.clear()
3572                G2IO.ProjFileOpen(self)
3573                Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3574                self.PatternTree.SelectItem(Id)
3575
3576        finally:
3577            dlg.Destroy()
3578       
3579    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3580        'Display an error message'
3581        result = None
3582        if parent is None:
3583            dlg = wx.MessageDialog(self, message, title,  wtype)
3584        else:
3585            dlg = wx.MessageDialog(parent, message, title,  wtype)
3586            dlg.CenterOnParent() # not working on Mac
3587        try:
3588            result = dlg.ShowModal()
3589        finally:
3590            dlg.Destroy()
3591        return result
3592
3593class GSASIImain(wx.App):
3594    '''Defines a wxApp for GSAS-II
3595
3596    Creates a wx frame (self.main) which contains the display of the
3597    data tree.
3598    '''
3599    def OnInit(self):
3600        '''Called automatically when the app is created.'''
3601        if '2.7' not in sys.version[:5]:
3602            dlg = wx.MessageDialog(None, 
3603                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version[:5],
3604                'Python version error',  wx.OK)
3605            try:
3606                result = dlg.ShowModal()
3607            finally:
3608                dlg.Destroy()
3609            sys.exit()
3610        self.main = GSASII(None)
3611        self.main.Show()
3612        self.SetTopWindow(self.main)
3613        # DEBUG: jump to sequential results
3614        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3615        #self.main.PatternTree.SelectItem(Id)
3616        # end DEBUG
3617        return True
3618    # def MacOpenFile(self, filename):
3619    #     '''Called on Mac every time a file is dropped on the app when it is running,
3620    #     treat this like a File/Open project menu action.
3621    #     Should be ignored on other platforms
3622    #     '''
3623    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3624    #     print 'MacOpen',filename
3625    #     if os.path.splitext(filename)[1] == '.py': return
3626    #     # end PATCH
3627    #     self.main.OnFileOpen(None,filename)
3628    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3629   
3630def main():
3631    '''Start up the GSAS-II application'''
3632    #application = GSASIImain() # don't redirect output, someday we
3633    # may want to do this if we can
3634    application = GSASIImain(0)
3635    if wxInspector:
3636        import wx.lib.inspection as wxeye
3637        wxeye.InspectionTool().Show()
3638
3639    #application.main.OnRefine(None)
3640    application.MainLoop()
3641   
3642if __name__ == '__main__':
3643    # print versions
3644    print "Python module versions loaded:"
3645    print "python:     ",sys.version[:5]
3646    print "wxpython:   ",wx.__version__
3647    print "matplotlib: ",mpl.__version__
3648    print "numpy:      ",np.__version__
3649    print "scipy:      ",sp.__version__
3650    print "OpenGL:     ",ogl.__version__
3651    try:
3652        from PIL import Image
3653        try:
3654            from PIL import PILLOW_VERSION
3655            version = PILLOW_VERSION
3656        except:
3657            version = Image.VERSION
3658        print "pillow:     ",version
3659    except ImportError:
3660        try:
3661            import Image
3662            print "Image (PIL):",Image.VERSION
3663        except ImportError:
3664            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3665    try:
3666        import mkl
3667        print "Max threads ",mkl.get_max_threads()
3668    except:
3669        pass
3670    import platform
3671    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3672    #print "wxPython description",wx.PlatformInfo
3673    print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
3674    GSASIIpath.InvokeDebugOpts()
3675    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.