source: trunk/GSASII.py @ 1918

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

further mods on twins

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