source: trunk/GSASII.py @ 1593

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

make ifX20 a G2frame variable
add Progress bar to findMV
force modulation values to 0-1 range
fix various bugs in super lattice stuff

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