source: trunk/GSASII.py @ 1453

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

get HKLF data type into RefDict?
create a SetDefaultDData routine in GSASII.py
fix copyflags for sc extinction coeff
fix neutron resonant ff for TOF
fix error in making Hessian v-cov matrix - now matches the Jabobian one
put names in the Es, Ep & Eg sc extinction coeff
fix errors in SCExtinction - still problem with derivatives

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