source: trunk/GSASII.py @ 1619

Last change on this file since 1619 was 1619, checked in by toby, 7 years ago

add columnsorter for hetrogeneous seq ref; reorg to start moving widgets out of g2grid

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