source: trunk/GSASII.py @ 1715

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

make plot styles global instead of individual; now qplot, dplot work for multiplots

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