source: trunk/GSASII.py @ 1909

Last change on this file since 1909 was 1909, checked in by vondreele, 8 years ago

fix import names for HKLF files - now explicit Shelx names
fix importing problems with run-on numbers in Shelx HKLF 4 & 5 files
fix issues with importing structure factors before importing a phase

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