source: trunk/GSASII.py @ 1694

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

some missing stuff in PWDR[0] dictionary
add new fxn to G2img - meanAZm - calc effective azimuth for a azm range for correct polarization calc.
fix some FlexGridSizers? in G2phsGUI
fix '+'/'=' key response for Anaconda vs Enthought versions; one sees '=' the other sees '+'!
fix L/R shifting in waterfall plots.

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