source: trunk/GSASII.py @ 1688

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

allow non-identical sequential refinement when copynext is not used (still bugs on table display); implement tutorial downloads

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