source: trunk/GSASII.py @ 1971

Last change on this file since 1971 was 1971, checked in by toby, 6 years ago

Add preferences option to edit configuration options

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