source: trunk/GSASII.py @ 1780

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

force collapse of all children when main tree item is collapsed
begin texture fitting from seq. refinements
fix bugs in plotting if all SH coeff = 0
set background on Fcsq when delt = 3sig & 10 sig

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