source: branch/logging/GSASII.py @ 1477

Last change on this file since 1477 was 1477, checked in by toby, 8 years ago

variable change logging finally works

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 158.4 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2014-08-25 01:57:46 +0000 (Mon, 25 Aug 2014) $
6# $Author: toby $
7# $Revision: 1477 $
8# $URL: branch/logging/GSASII.py $
9# $Id: GSASII.py 1477 2014-08-25 01:57:46Z toby $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. Restarting GSAS-II')
59    print('*******************************************************')         
60    loc = os.path.dirname(__file__)
61    subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
62    sys.exit()
63   
64# load the GSAS routines
65import GSASIIpath
66GSASIIpath.SetVersionNumber("$Revision: 1477 $")
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        import log
1849        self.PatternTree = log.G2TreeCtrl(id=wxID_PATTERNTREE,
1850            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1851        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,
1852            self.OnPatternTreeSelChanged, id=wxID_PATTERNTREE)
1853        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1854            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1855        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1856            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1857        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1858            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1859        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1860            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1861        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
1862        self.root = self.PatternTree.root
1863       
1864        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1865            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1866        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1867        plotFrame.Show()
1868       
1869        self.dataDisplay = None
1870       
1871    def __init__(self, parent):
1872        self.ExportLookup = {}
1873        self._init_ctrls(parent)
1874        self.Image = wx.Image(
1875            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
1876            wx.BITMAP_TYPE_ICO)
1877        if "wxMSW" in wx.PlatformInfo:
1878            img = self.Image.Scale(16, 16).ConvertToBitmap()
1879        elif "wxGTK" in wx.PlatformInfo:
1880            img = self.Image.Scale(22, 22).ConvertToBitmap()
1881        else:
1882            img = self.Image.ConvertToBitmap()
1883        self.SetIcon(wx.IconFromBitmap(img))
1884        self.Bind(wx.EVT_CLOSE, self.ExitMain)
1885        # various defaults
1886        self.oldFocus = None
1887        self.GSASprojectfile = ''
1888        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
1889        self.exportDir = None  # the last directory used for exports, if any.
1890        self.undofile = ''
1891        self.TreeItemDelete = False
1892        self.Offset = [0.0,0.0]
1893        self.delOffset = .02
1894        self.refOffset = -100.0
1895        self.refDelt = .01
1896        self.Weight = False
1897        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
1898        self.IfPlot = False
1899        self.PatternId = 0
1900        self.PickId = 0
1901        self.PeakTable = []
1902        self.LimitsTable = []
1903        self.HKL = []
1904        self.Lines = []
1905        self.itemPicked = None
1906        self.dataFrame = None
1907        self.Interpolate = 'nearest'
1908        self.ContourColor = 'Paired'
1909        self.VcovColor = 'RdYlGn'
1910        self.RamaColor = 'Blues'
1911        self.Projection = 'equal area'
1912        self.logPlot = False
1913        self.qPlot = False
1914        self.sqPlot = False
1915        self.SqrtPlot = False
1916        self.ErrorBars = False
1917        self.Contour = False
1918        self.Legend = False
1919        self.SinglePlot = True
1920        self.SubBack = False
1921        self.seqReverse = False
1922        self.plotView = 0
1923        self.Image = 0
1924        self.oldImagefile = ''
1925        self.ImageZ = []
1926        self.Integrate = 0
1927        self.imageDefault = {}
1928        self.Sngl = False
1929        self.ifGetRing = False
1930        self.MaskKey = ''           #trigger for making image masks
1931        self.StrainKey = ''         #ditto for new strain d-zeros
1932        self.EnablePlot = True
1933        self.Tutorials = False      #used for changing default directory
1934        arg = sys.argv
1935        if len(arg) > 1:
1936            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1937            self.dirname = os.path.dirname(arg[1])
1938            if self.dirname: os.chdir(self.dirname)
1939            try:
1940                G2IO.ProjFileOpen(self)
1941                self.PatternTree.Expand(self.root)
1942                for item in self.Refine: item.Enable(True)
1943                self.EnableSeqRefineMenu()
1944
1945            except:
1946                print 'Error opening file',arg[1]
1947
1948    def OnSize(self,event):
1949        'Called when the main window is resized. Not sure why'
1950        w,h = self.GetClientSizeTuple()
1951        self.mainPanel.SetSize(wx.Size(w,h))
1952        self.PatternTree.SetSize(wx.Size(w,h))
1953                       
1954    def OnPatternTreeSelChanged(self, event):
1955        '''Called when a data tree item is selected'''
1956        if self.TreeItemDelete:
1957            self.TreeItemDelete = False
1958        else:
1959            pltNum = self.G2plotNB.nb.GetSelection()
1960            if pltNum >= 0:                         #to avoid the startup with no plot!
1961                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1962                pltPlot = pltPage.figure
1963            item = event.GetItem()
1964            G2gd.MovePatternTreeToGrid(self,item)
1965            if self.oldFocus:
1966                self.oldFocus.SetFocus()
1967       
1968    def OnPatternTreeItemCollapsed(self, event):
1969        'Called when a tree item is collapsed'
1970        event.Skip()
1971
1972    def OnPatternTreeItemExpanded(self, event):
1973        'Called when a tree item is expanded'
1974        event.Skip()
1975       
1976    def OnPatternTreeItemDelete(self, event):
1977        'Called when a tree item is deleted -- not sure what this does'
1978        self.TreeItemDelete = True
1979
1980    def OnPatternTreeItemActivated(self, event):
1981        'Called when a tree item is activated'
1982        event.Skip()
1983       
1984    def OnPatternTreeKeyDown(self,event):
1985        'Not sure what this does'
1986        key = event.GetKeyCode()
1987        item = self.PickId
1988        if type(item) is int: return # is this the toplevel in tree?
1989        if key == wx.WXK_UP:
1990            self.oldFocus = wx.Window.FindFocus()
1991            self.PatternTree.GetPrevSibling(item)
1992        elif key == wx.WXK_DOWN:
1993            self.oldFocus = wx.Window.FindFocus()
1994            self.PatternTree.GetNextSibling(item)
1995               
1996    def OnReadPowderPeaks(self,event):
1997        'Bound to menu Data/Read Powder Peaks -- still needed?'
1998        Cuka = 1.54052
1999        self.CheckNotebook()
2000        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
2001            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
2002        try:
2003            if dlg.ShowModal() == wx.ID_OK:
2004                self.HKL = []
2005                self.powderfile = dlg.GetPath()
2006                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
2007                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2008                data = ['PKS',Cuka,0.0]
2009                names = ['Type','Lam','Zero'] 
2010                codes = [0,0,0]
2011                inst = [G2IO.makeInstDict(names,data,codes),{}]
2012                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2013                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2014                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2015                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2016                self.PatternTree.Expand(Id)
2017                self.PatternTree.SelectItem(Id)
2018                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2019        finally:
2020            dlg.Destroy()
2021                       
2022    def OnImageRead(self,event):
2023        'Called to read in an image in any known format'
2024        self.CheckNotebook()
2025        dlg = wx.FileDialog(
2026            self, 'Choose image files', '.', '',
2027            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png)|'
2028            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png;*.zip|'
2029            'European detector file (*.edf)|*.edf|'
2030            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2031            'MAR file (*.mar*)|*.mar*|'
2032            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2033            'ADSC Image (*.img)|*.img|'
2034            'GSAS-II Image (*.G2img)|*.G2img|'
2035            'Portable Network Graphics image (*.png)|*.png|'
2036            'Zip archive (*.zip)|*.zip|'
2037            'All files (*.*)|*.*',
2038            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
2039        try:
2040            if dlg.ShowModal() == wx.ID_OK:
2041                imagefiles = dlg.GetPaths()
2042                imagefiles.sort()
2043                for imagefile in imagefiles:
2044                    # if a zip file, open and extract
2045                    if os.path.splitext(imagefile)[1].lower() == '.zip':
2046                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
2047                        if extractedfile is not None and extractedfile != imagefile:
2048                            imagefile = extractedfile
2049                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
2050                    if Comments:
2051                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
2052                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2053                        Imax = np.amax(Image)
2054                        Imin = max(0.0,np.amin(Image))          #force positive
2055                        if self.imageDefault:
2056                            Data = copy.copy(self.imageDefault)
2057                            Data['showLines'] = True
2058                            Data['ring'] = []
2059                            Data['rings'] = []
2060                            Data['cutoff'] = 10
2061                            Data['pixLimit'] = 20
2062                            Data['edgemin'] = 100000000
2063                            Data['calibdmin'] = 0.5
2064                            Data['calibskip'] = 0
2065                            Data['ellipses'] = []
2066                            Data['calibrant'] = ''
2067                            Data['GonioAngles'] = [0.,0.,0.]
2068                            Data['DetDepthRef'] = False
2069                        else:
2070                            Data['type'] = 'PWDR'
2071                            Data['color'] = 'Paired'
2072                            Data['tilt'] = 0.0
2073                            Data['rotation'] = 0.0
2074                            Data['showLines'] = False
2075                            Data['ring'] = []
2076                            Data['rings'] = []
2077                            Data['cutoff'] = 10
2078                            Data['pixLimit'] = 20
2079                            Data['calibdmin'] = 0.5
2080                            Data['calibskip'] = 0
2081                            Data['edgemin'] = 100000000
2082                            Data['ellipses'] = []
2083                            Data['GonioAngles'] = [0.,0.,0.]
2084                            Data['DetDepth'] = 0.
2085                            Data['DetDepthRef'] = False
2086                            Data['calibrant'] = ''
2087                            Data['IOtth'] = [2.0,5.0]
2088                            Data['LRazimuth'] = [135,225]
2089                            Data['azmthOff'] = 0.0
2090                            Data['outChannels'] = 2500
2091                            Data['outAzimuths'] = 1
2092                            Data['centerAzm'] = False
2093                            Data['fullIntegrate'] = False
2094                            Data['setRings'] = False
2095                            Data['background image'] = ['',-1.0]                           
2096                            Data['dark image'] = ['',-1.0]
2097                        Data['setDefault'] = False
2098                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2099                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
2100                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2101                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2102                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2103                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2104                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
2105                        self.PickId = Id
2106                        self.Image = Id
2107                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!               
2108                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
2109        finally:
2110            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2111            os.chdir(path)
2112            dlg.Destroy()
2113
2114    def CheckNotebook(self):
2115        '''Make sure the data tree has the minimally expected controls.
2116        (BHT) correct?
2117        '''
2118        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2119            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2120            self.PatternTree.SetItemPyData(sub,[''])
2121        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2122            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2123            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2124        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2125            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2126            self.PatternTree.SetItemPyData(sub,{})
2127        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2128            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2129            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2130        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2131            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2132            self.PatternTree.SetItemPyData(sub,{})
2133        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2134            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2135            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2136                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2137               
2138    class CopyDialog(wx.Dialog):
2139        '''Creates a dialog for copying control settings between
2140        data tree items'''
2141        def __init__(self,parent,title,text,data):
2142            wx.Dialog.__init__(self,parent,-1,title, 
2143                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2144            self.data = data
2145            panel = wx.Panel(self)
2146            mainSizer = wx.BoxSizer(wx.VERTICAL)
2147            topLabl = wx.StaticText(panel,-1,text)
2148            mainSizer.Add((10,10),1)
2149            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2150            mainSizer.Add((10,10),1)
2151            ncols = len(data)/40+1
2152            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2153            for id,item in enumerate(self.data):
2154                ckbox = wx.CheckBox(panel,id,item[1])
2155                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2156                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2157            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2158            OkBtn = wx.Button(panel,-1,"Ok")
2159            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2160            cancelBtn = wx.Button(panel,-1,"Cancel")
2161            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2162            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2163            btnSizer.Add((20,20),1)
2164            btnSizer.Add(OkBtn)
2165            btnSizer.Add((20,20),1)
2166            btnSizer.Add(cancelBtn)
2167            btnSizer.Add((20,20),1)
2168           
2169            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2170            panel.SetSizer(mainSizer)
2171            panel.Fit()
2172            self.Fit()
2173       
2174        def OnCopyChange(self,event):
2175            id = event.GetId()
2176            self.data[id][0] = self.FindWindowById(id).GetValue()       
2177           
2178        def OnOk(self,event):
2179            parent = self.GetParent()
2180            parent.Raise()
2181            self.EndModal(wx.ID_OK)             
2182           
2183        def OnCancel(self,event):
2184            parent = self.GetParent()
2185            parent.Raise()
2186            self.EndModal(wx.ID_CANCEL)             
2187           
2188        def GetData(self):
2189            return self.data
2190       
2191    class SumDialog(wx.Dialog):
2192        'Allows user to supply scale factor(s) when summing data'
2193        def __init__(self,parent,title,text,dataType,data):
2194            wx.Dialog.__init__(self,parent,-1,title, 
2195                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2196            self.data = data
2197            panel = wx.Panel(self)
2198            mainSizer = wx.BoxSizer(wx.VERTICAL)
2199            topLabl = wx.StaticText(panel,-1,text)
2200            mainSizer.Add((10,10),1)
2201            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2202            mainSizer.Add((10,10),1)
2203            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2204            for id,item in enumerate(self.data[:-1]):
2205                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2206                name.SetEditable(False)
2207                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2208                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2209                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2210                dataGridSizer.Add(scale,0,wx.LEFT,10)
2211                dataGridSizer.Add(name,0,wx.RIGHT,10)
2212            if dataType:
2213                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2214                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2215                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2216                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2217                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2218                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2219            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2220            OkBtn = wx.Button(panel,-1,"Ok")
2221            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2222            cancelBtn = wx.Button(panel,-1,"Cancel")
2223            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2224            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2225            btnSizer.Add((20,20),1)
2226            btnSizer.Add(OkBtn)
2227            btnSizer.Add((20,20),1)
2228            btnSizer.Add(cancelBtn)
2229            btnSizer.Add((20,20),1)
2230           
2231            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2232            panel.SetSizer(mainSizer)
2233            panel.Fit()
2234            self.Fit()
2235
2236        def OnScaleChange(self,event):
2237            id = event.GetId()
2238            value = self.FindWindowById(id).GetValue()
2239            try:
2240                self.data[id][0] = float(value)
2241                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2242            except ValueError:
2243                if value and '-' not in value[0]:
2244                    print 'bad input - numbers only'
2245                    self.FindWindowById(id).SetValue('0.000')
2246           
2247        def OnNameChange(self,event):
2248            self.data[-1] = self.name.GetValue() 
2249           
2250        def OnOk(self,event):
2251            parent = self.GetParent()
2252            parent.Raise()
2253            self.EndModal(wx.ID_OK)             
2254           
2255        def OnCancel(self,event):
2256            parent = self.GetParent()
2257            parent.Raise()
2258            self.EndModal(wx.ID_CANCEL)             
2259           
2260        def GetData(self):
2261            return self.data
2262                       
2263    def OnPwdrSum(self,event):
2264        'Sum together powder data(?)'
2265        TextList = []
2266        DataList = []
2267        SumList = []
2268        Names = []
2269        Inst = None
2270        SumItemList = []
2271        Comments = ['Sum equals: \n']
2272        if self.PatternTree.GetCount():
2273            item, cookie = self.PatternTree.GetFirstChild(self.root)
2274            while item:
2275                name = self.PatternTree.GetItemText(item)
2276                Names.append(name)
2277                if 'PWDR' in name:
2278                    TextList.append([0.0,name])
2279                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2280                    if not Inst:
2281                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2282                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2283            if len(TextList) < 2:
2284                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2285                return
2286            TextList.append('default_sum_name')               
2287            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2288            try:
2289                if dlg.ShowModal() == wx.ID_OK:
2290                    lenX = 0
2291                    Xminmax = [0,0]
2292                    Xsum = []
2293                    Ysum = []
2294                    Vsum = []
2295                    result = dlg.GetData()
2296                    for i,item in enumerate(result[:-1]):
2297                        scale,name = item
2298                        data = DataList[i]
2299                        if scale:
2300                            Comments.append("%10.3f %s" % (scale,' * '+name))
2301                            x,y,w,yc,yb,yd = data   #numpy arrays!
2302                            v = 1./w
2303                            if lenX:
2304                                if lenX != len(x):
2305                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2306                                        '\nExpected:'+str(lenX)+ \
2307                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2308                                    return
2309                            else:
2310                                lenX = len(x)
2311                            if Xminmax[1]:
2312                                if Xminmax != [x[0],x[-1]]:
2313                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2314                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2315                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2316                                    return
2317                                else:
2318                                    for j,yi in enumerate(y):
2319                                         Ysum[j] += scale*yi
2320                                         Vsum[j] += abs(scale)*v[j]
2321                            else:
2322                                Xminmax = [x[0],x[-1]]
2323                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2324                                for j,yi in enumerate(y):
2325                                    Xsum.append(x[j])
2326                                    Ysum.append(scale*yi)
2327                                    Vsum.append(abs(scale*v[j]))
2328                    Wsum = 1./np.array(Vsum)
2329                    outname = 'PWDR '+result[-1]
2330                    Id = 0
2331                    if outname in Names:
2332                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2333                        try:
2334                            if dlg2.ShowModal() == wx.ID_OK:
2335                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2336                                self.PatternTree.Delete(Id)
2337                        finally:
2338                            dlg2.Destroy()
2339                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2340                    if Id:
2341                        Sample = G2pdG.SetDefaultSample()
2342                        valuesdict = {
2343                            'wtFactor':1.0,
2344                            'Dummy':False,
2345                            'ranId':ran.randint(0,sys.maxint),
2346                            }
2347                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2348                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2349                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2350                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2351                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2352                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2353                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2354                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2355                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2356                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2357                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2358                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2359                        self.PatternTree.SelectItem(Id)
2360                        self.PatternTree.Expand(Id)
2361            finally:
2362                dlg.Destroy()
2363
2364    def OnImageSum(self,event):
2365        'Sum together image data(?)'
2366        TextList = []
2367        DataList = []
2368        SumList = []
2369        Names = []
2370        Inst = []
2371        SumItemList = []
2372        Comments = ['Sum equals: \n']
2373        if self.PatternTree.GetCount():
2374            item, cookie = self.PatternTree.GetFirstChild(self.root)
2375            while item:
2376                name = self.PatternTree.GetItemText(item)
2377                Names.append(name)
2378                if 'IMG' in name:
2379                    TextList.append([0.0,name])
2380                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2381                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2382                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2383            if len(TextList) < 2:
2384                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2385                return
2386            TextList.append('default_sum_name')               
2387            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2388            try:
2389                if dlg.ShowModal() == wx.ID_OK:
2390                    imSize = 0
2391                    result = dlg.GetData()
2392                    First = True
2393                    Found = False
2394                    for i,item in enumerate(result[:-1]):
2395                        scale,name = item
2396                        data = DataList[i]
2397                        if scale:
2398                            Found = True                               
2399                            Comments.append("%10.3f %s" % (scale,' * '+name))
2400                            Npix,imagefile = data
2401                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2402                            if First:
2403                                newImage = np.zeros_like(image)
2404                                First = False
2405                            if imSize:
2406                                if imSize != Npix:
2407                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2408                                        '\nExpected:'+str(imSize)+ \
2409                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2410                                    return
2411                                newImage = newImage+scale*image
2412                            else:
2413                                imSize = Npix
2414                                newImage = newImage+scale*image
2415                            del(image)
2416                    if not Found:
2417                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2418                        return
2419                       
2420                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2421                    outname = 'IMG '+result[-1]
2422                    Id = 0
2423                    if outname in Names:
2424                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2425                        try:
2426                            if dlg2.ShowModal() == wx.ID_OK:
2427                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2428                        finally:
2429                            dlg2.Destroy()
2430                    else:
2431                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2432                    if Id:
2433                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2434                            'G2img files (*.G2img)|*.G2img', 
2435                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2436                        if dlg.ShowModal() == wx.ID_OK:
2437                            newimagefile = dlg.GetPath()
2438                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2439                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2440                            Imax = np.amax(newImage)
2441                            Imin = np.amin(newImage)
2442                            newImage = []
2443                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2444                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2445                        del(newImage)
2446                        if self.imageDefault:
2447                            Data = copy.copy(self.imageDefault)
2448                        Data['showLines'] = True
2449                        Data['ring'] = []
2450                        Data['rings'] = []
2451                        Data['cutoff'] = 10
2452                        Data['pixLimit'] = 20
2453                        Data['ellipses'] = []
2454                        Data['calibrant'] = ''
2455                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2456                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2457                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2458                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2459                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2460                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2461                        self.PatternTree.SelectItem(Id)
2462                        self.PatternTree.Expand(Id)
2463                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2464                        self.Image = self.PickId
2465            finally:
2466                dlg.Destroy()
2467                     
2468    def OnAddPhase(self,event):
2469        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2470        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2471            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2472        else:
2473            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2474        PhaseName = ''
2475        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2476            style=wx.OK)
2477        if dlg.ShowModal() == wx.ID_OK:
2478            PhaseName = dlg.GetValue()
2479        dlg.Destroy()
2480        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2481        E,SGData = G2spc.SpcGroup('P 1')
2482        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2483       
2484    def OnDeletePhase(self,event):
2485        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2486        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2487        if self.dataFrame:
2488            self.dataFrame.Clear() 
2489        TextList = []
2490        DelList = []
2491        DelItemList = []
2492        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2493            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2494        else:
2495            return
2496        if sub:
2497            item, cookie = self.PatternTree.GetFirstChild(sub)
2498            while item:
2499                TextList.append(self.PatternTree.GetItemText(item))
2500                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2501            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2502            try:
2503                if dlg.ShowModal() == wx.ID_OK:
2504                    result = dlg.GetSelections()
2505                    for i in result: DelList.append([i,TextList[i]])
2506                    item, cookie = self.PatternTree.GetFirstChild(sub)
2507                    i = 0
2508                    while item:
2509                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2510                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2511                        i += 1
2512                    for item in DelItemList:
2513                        name = self.PatternTree.GetItemText(item)
2514                        self.PatternTree.Delete(item)
2515                        self.G2plotNB.Delete(name)
2516                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2517                    while item:
2518                        name = self.PatternTree.GetItemText(item)
2519                        if 'PWDR' in name:
2520                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2521                            refList = self.PatternTree.GetItemPyData(Id)
2522                            for i,item in DelList:
2523                                del(refList[item])
2524                            self.PatternTree.SetItemPyData(Id,refList)
2525                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2526            finally:
2527                dlg.Destroy()
2528               
2529    def OnRenameData(self,event):
2530        'Renames an existing phase. Called by Data/Rename Phase menu'
2531        name = self.PatternTree.GetItemText(self.PickId)     
2532        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2533            dataType = name[:name.index(' ')+1]                 #includes the ' '
2534            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2535                defaultValue=name[name.index(' ')+1:])
2536            try:
2537                if dlg.ShowModal() == wx.ID_OK:
2538                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2539            finally:
2540                dlg.Destroy()
2541       
2542    def GetFileList(self,fileType,skip=None):        #potentially useful?
2543        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2544        fileList = []
2545        Source = ''
2546        id, cookie = self.PatternTree.GetFirstChild(self.root)
2547        while id:
2548            name = self.PatternTree.GetItemText(id)
2549            if fileType in name:
2550                if id == skip:
2551                    Source = name
2552                else:
2553                    fileList.append([False,name,id])
2554            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2555        if skip:
2556            return fileList,Source
2557        else:
2558            return fileList
2559           
2560    def OnDataDelete(self, event):
2561        '''Delete one or more histograms from data tree. Called by the
2562        Data/DeleteData menu
2563        '''
2564        TextList = ['All Data']
2565        DelList = []
2566        DelItemList = []
2567        ifPWDR = False
2568        ifSASD = False
2569        ifIMG = False
2570        ifHKLF = False
2571        ifPDF = False
2572        if self.PatternTree.GetCount():
2573            item, cookie = self.PatternTree.GetFirstChild(self.root)
2574            while item:
2575                name = self.PatternTree.GetItemText(item)
2576                if name not in ['Notebook','Controls','Covariance','Constraints',
2577                    'Restraints','Phases','Rigid bodies']:
2578                    if 'PWDR' in name: ifPWDR = True
2579                    if 'SASD' in name: ifSASD = True
2580                    if 'IMG' in name: ifIMG = True
2581                    if 'HKLF' in name: ifHKLF = True
2582                    if 'PDF' in name: ifPDF = True
2583                    TextList.append(name)
2584                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2585            if ifPWDR: TextList.insert(1,'All PWDR')
2586            if ifSASD: TextList.insert(1,'All SASD')
2587            if ifIMG: TextList.insert(1,'All IMG')
2588            if ifHKLF: TextList.insert(1,'All HKLF')
2589            if ifPDF: TextList.insert(1,'All PDF')               
2590            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2591            try:
2592                if dlg.ShowModal() == wx.ID_OK:
2593                    result = dlg.GetSelections()
2594                    for i in result: DelList.append(TextList[i])
2595                    if 'All Data' in DelList:
2596                        DelList = [item for item in TextList if item[:3] != 'All']
2597                    elif 'All PWDR' in DelList:
2598                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2599                    elif 'All SASD' in DelList:
2600                        DelList = [item for item in TextList if item[:4] == 'SASD']
2601                    elif 'All IMG' in DelList:
2602                        DelList = [item for item in TextList if item[:3] == 'IMG']
2603                    elif 'All HKLF' in DelList:
2604                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2605                    elif 'All PDF' in DelList:
2606                        DelList = [item for item in TextList if item[:3] == 'PDF']
2607                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2608                    while item:
2609                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2610                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2611                    for item in DelItemList:
2612                        self.PatternTree.Delete(item)
2613                    self.PickId = 0
2614                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2615            finally:
2616                dlg.Destroy()
2617
2618    def OnFileOpen(self, event, filename=None):
2619        '''Reads in a GSAS-II .gpx project file in response to the
2620        File/Open Project menu button
2621        '''
2622        result = wx.ID_OK
2623        Id = 0
2624        self.EnablePlot = False
2625        if self.PatternTree.GetChildrenCount(self.root,False):
2626            if self.dataFrame:
2627                self.dataFrame.Clear() 
2628            dlg = wx.MessageDialog(
2629                self,
2630                'Do you want to overwrite the current project? '
2631                'Any unsaved changes will be lost. Press OK to continue.',
2632                'Overwrite?',  wx.OK | wx.CANCEL)
2633            try:
2634                result = dlg.ShowModal()
2635                if result == wx.ID_OK:
2636                    self.PatternTree.DeleteChildren(self.root)
2637                    self.GSASprojectfile = ''
2638                    if self.HKL: self.HKL = []
2639                    if self.G2plotNB.plotList:
2640                        self.G2plotNB.clear()
2641            finally:
2642                dlg.Destroy()
2643        if result != wx.ID_OK: return
2644
2645        if not filename:
2646            if self.dataDisplay: self.dataDisplay.Destroy()
2647            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2648                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2649            try:
2650                if dlg.ShowModal() != wx.ID_OK: return
2651                self.GSASprojectfile = dlg.GetPath()
2652                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2653                self.dirname = dlg.GetDirectory()
2654            finally:
2655                dlg.Destroy()
2656        else:
2657            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2658            self.dirname = os.path.split(filename)[0]
2659
2660        G2IO.ProjFileOpen(self)
2661        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2662        self.PatternTree.Expand(self.root)
2663        self.HKL = []
2664        item, cookie = self.PatternTree.GetFirstChild(self.root)
2665        while item and not Id:
2666            name = self.PatternTree.GetItemText(item)
2667            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
2668                Id = item
2669            elif name == 'Controls':
2670                data = self.PatternTree.GetItemPyData(item)
2671                if data:
2672                    for item in self.Refine: item.Enable(True)
2673                    self.EnableSeqRefineMenu()
2674            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2675        if Id:
2676            self.EnablePlot = True
2677            self.PatternTree.SelectItem(Id)
2678        self.CheckNotebook()
2679        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2680
2681    def OnFileClose(self, event):
2682        '''Clears the data tree in response to the
2683        File/New Project menu button. User is given option to save
2684        the project.
2685        '''
2686        if self.dataFrame:
2687            self.dataFrame.Clear()
2688            self.dataFrame.SetLabel('GSAS-II data display') 
2689        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2690        try:
2691            result = dlg.ShowModal()
2692            if result == wx.ID_OK:
2693                self.OnFileSaveMenu(event)
2694            if result != wx.ID_CANCEL:
2695                self.GSASprojectfile = ''
2696                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2697                self.PatternTree.DeleteChildren(self.root)
2698                if self.HKL: self.HKL = []
2699                if self.G2plotNB.plotList:
2700                    self.G2plotNB.clear()
2701        finally:
2702            dlg.Destroy()
2703
2704    def OnFileSave(self, event):
2705        '''Save the current project in response to the
2706        File/Save Project menu button
2707        '''
2708       
2709        if self.GSASprojectfile: 
2710            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2711            G2IO.ProjFileSave(self)
2712        else:
2713            self.OnFileSaveas(event)
2714
2715    def OnFileSaveas(self, event):
2716        '''Save the current project in response to the
2717        File/Save as menu button
2718        '''
2719        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2720            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2721        try:
2722            if dlg.ShowModal() == wx.ID_OK:
2723                self.GSASprojectfile = dlg.GetPath()
2724                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2725                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2726                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2727                G2IO.ProjFileSave(self)
2728                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2729        finally:
2730            dlg.Destroy()
2731
2732    def ExitMain(self, event):
2733        '''Called if the main window is closed'''
2734        if self.undofile:
2735            os.remove(self.undofile)
2736        sys.exit()
2737       
2738    def OnFileExit(self, event):
2739        '''Called in response to the File/Quit menu button'''
2740        if self.dataFrame:
2741            self.dataFrame.Clear() 
2742            self.dataFrame.Destroy()
2743        self.Close()
2744       
2745    # def OnExportPatterns(self,event):
2746    #     names = ['All']
2747    #     exports = []
2748    #     item, cookie = self.PatternTree.GetFirstChild(self.root)
2749    #     while item:
2750    #         name = self.PatternTree.GetItemText(item)
2751    #         if 'PWDR' in name:
2752    #             names.append(name)
2753    #         item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2754    #     if names:
2755    #         dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2756    #         if dlg.ShowModal() == wx.ID_OK:
2757    #             sel = dlg.GetSelections()
2758    #             if sel[0] == 0:
2759    #                 exports = names[1:]
2760    #             else:
2761    #                 for x in sel:
2762    #                     exports.append(names[x])
2763    #         dlg.Destroy()
2764    #     if exports:
2765    #         dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '',
2766    #             'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2767    #             wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2768    #         try:
2769    #             if dlg.ShowModal() == wx.ID_OK:
2770    #                 powderfile = dlg.GetPath()
2771    #                 powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2772    #                 if 'fxye' in powderfile:
2773    #                     G2IO.powderFxyeSave(self,exports,powderfile)
2774    #                 else:       #just xye
2775    #                     G2IO.powderXyeSave(self,exports,powderfile)
2776    #         finally:
2777    #             dlg.Destroy()
2778       
2779    def OnExportPeakList(self,event):
2780        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2781            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2782        try:
2783            if dlg.ShowModal() == wx.ID_OK:
2784                self.peaklistfile = dlg.GetPath()
2785                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2786                file = open(self.peaklistfile,'w')               
2787                item, cookie = self.PatternTree.GetFirstChild(self.root)
2788                while item:
2789                    name = self.PatternTree.GetItemText(item)
2790                    if 'PWDR' in name:
2791                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2792                        while item2:
2793                            name2 = self.PatternTree.GetItemText(item2)
2794                            if name2 == 'Peak List':
2795                                peaks = self.PatternTree.GetItemPyData(item2)['peaks']
2796                                file.write("%s \n" % (name+' Peak List'))               
2797                                for peak in peaks:
2798                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2799                                        (peak[0],peak[2],peak[4],peak[6]))
2800                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2801                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2802                file.close()
2803        finally:
2804            dlg.Destroy()
2805       
2806    def OnExportHKL(self,event):
2807        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2808            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2809        try:
2810            if dlg.ShowModal() == wx.ID_OK:
2811                self.peaklistfile = dlg.GetPath()
2812                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2813                file = open(self.peaklistfile,'w')               
2814                item, cookie = self.PatternTree.GetFirstChild(self.root)
2815                while item:
2816                    name = self.PatternTree.GetItemText(item)
2817                    if 'PWDR' in name:
2818                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2819                        while item2:
2820                            name2 = self.PatternTree.GetItemText(item2)
2821                            if name2 == 'Reflection Lists':
2822                                data = self.PatternTree.GetItemPyData(item2)
2823                                phases = data.keys()
2824                                for phase in phases:
2825                                    peaks = data[phase]
2826                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2827                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2828                                    for peak in peaks:
2829                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2830                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2831                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2832                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2833                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2834                file.close()
2835        finally:
2836            dlg.Destroy()
2837       
2838    def OnExportPDF(self,event):
2839        #need S(Q) and G(R) to be saved here - probably best from selection?
2840        names = ['All']
2841        exports = []
2842        item, cookie = self.PatternTree.GetFirstChild(self.root)
2843        while item:
2844            name = self.PatternTree.GetItemText(item)
2845            if 'PDF' in name:
2846                names.append(name)
2847            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2848        if names:
2849            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2850            if dlg.ShowModal() == wx.ID_OK:
2851                sel = dlg.GetSelections()
2852                if sel[0] == 0:
2853                    exports = names[1:]
2854                else:
2855                    for x in sel:
2856                        exports.append(names[x])
2857            dlg.Destroy()
2858        if exports:
2859            G2IO.PDFSave(self,exports)
2860       
2861    def OnMakePDFs(self,event):
2862        '''Calculates PDFs
2863        '''
2864        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2865        TextList = ['All PWDR']
2866        PDFlist = []
2867        Names = []
2868        if self.PatternTree.GetCount():
2869            id, cookie = self.PatternTree.GetFirstChild(self.root)
2870            while id:
2871                name = self.PatternTree.GetItemText(id)
2872                Names.append(name)
2873                if 'PWDR' in name:
2874                    TextList.append(name)
2875                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2876            if len(TextList) == 1:
2877                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2878                return
2879            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2880            try:
2881                if dlg.ShowModal() == wx.ID_OK:
2882                    result = dlg.GetSelections()
2883                    for i in result: PDFlist.append(TextList[i])
2884                    if 0 in result:
2885                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2886                    for item in PDFlist:
2887                        PWDRname = item[4:]
2888                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2889                        Data = {
2890                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2891                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2892                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2893                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2894                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2895                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2896                            'Lorch':True,}
2897                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2898                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2899                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2900                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2901                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2902                for item in self.ExportPDF: item.Enable(True)
2903            finally:
2904                dlg.Destroy()
2905               
2906    def GetPWDRdatafromTree(self,PWDRname):
2907        ''' Returns powder data from GSASII tree
2908
2909        :param str PWDRname: a powder histogram name as obtained from
2910          :meth:`GSASIIstruct.GetHistogramNames`
2911
2912        :returns: PWDRdata = powder data dictionary with
2913          Powder data arrays, Limits, Instrument Parameters,
2914          Sample Parameters           
2915        '''
2916        PWDRdata = {}
2917        try:
2918            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2919        except ValueError:
2920            PWDRdata['wtFactor'] = 1.0
2921        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2922        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2923        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2924        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2925        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2926        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2927        if 'ranId' not in PWDRdata:  # patch, add a random Id
2928            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
2929        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
2930            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
2931        return PWDRdata
2932
2933    def GetHKLFdatafromTree(self,HKLFname):
2934        ''' Returns single crystal data from GSASII tree
2935
2936        :param str HKLFname: a single crystal histogram name as obtained
2937          from
2938          :meth:`GSASIIstruct.GetHistogramNames`
2939
2940        :returns: HKLFdata = single crystal data list of reflections
2941
2942        '''
2943        HKLFdata = {}
2944        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2945#        try:
2946#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2947#        except ValueError:
2948#            HKLFdata['wtFactor'] = 1.0
2949        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2950        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2951        return HKLFdata
2952       
2953    def GetPhaseData(self):
2954        '''Returns a dict with defined phases.
2955        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
2956        get same info from GPX file.
2957        '''
2958        phaseData = {}
2959        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2960            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2961        else:
2962            print 'no phases found in GetPhaseData'
2963            sub = None
2964        if sub:
2965            item, cookie = self.PatternTree.GetFirstChild(sub)
2966            while item:
2967                phaseName = self.PatternTree.GetItemText(item)
2968                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2969                if 'ranId' not in phaseData[phaseName]:
2970                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2971                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2972        return phaseData
2973
2974    def GetPhaseInfofromTree(self):
2975        '''Get the phase names and their rId values,
2976        also the histograms used in each phase.
2977
2978        :returns: (phaseRIdList, usedHistograms) where
2979
2980          * phaseRIdList is a list of random Id values for each phase
2981          * usedHistograms is a dict where the keys are the phase names
2982            and the values for each key are a list of the histogram names
2983            used in each phase.
2984        '''
2985        phaseRIdList = []
2986        usedHistograms = {}
2987        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2988        if sub:
2989            item, cookie = self.PatternTree.GetFirstChild(sub)
2990            while item:
2991                phaseName = self.PatternTree.GetItemText(item)
2992                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2993                if ranId: phaseRIdList.append(ranId)
2994                data = self.PatternTree.GetItemPyData(item)
2995                UseList = data['Histograms']
2996                usedHistograms[phaseName] = UseList.keys()
2997                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2998        return phaseRIdList,usedHistograms
2999
3000    def GetPhaseNames(self):
3001        '''Returns a list of defined phases.
3002        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3003        get same info from GPX file.
3004        '''
3005        phaseNames = []
3006        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3007            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3008        else:
3009            print 'no phases found in GetPhaseNames'
3010            sub = None
3011        if sub:
3012            item, cookie = self.PatternTree.GetFirstChild(sub)
3013            while item:
3014                phase = self.PatternTree.GetItemText(item)
3015                phaseNames.append(phase)
3016                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3017        return phaseNames
3018   
3019    def GetHistogramNames(self,hType):
3020        """ Returns a list of histogram names found in the GSASII data tree
3021        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3022        get same info from GPX file.
3023       
3024        :param str hType: list of histogram types
3025        :return: list of histogram names
3026       
3027        """
3028        HistogramNames = []
3029        if self.PatternTree.GetCount():
3030            item, cookie = self.PatternTree.GetFirstChild(self.root)
3031            while item:
3032                name = self.PatternTree.GetItemText(item)
3033                if name[:4] in hType:
3034                    HistogramNames.append(name)       
3035                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3036
3037        return HistogramNames
3038
3039                   
3040    def GetUsedHistogramsAndPhasesfromTree(self):
3041        ''' Returns all histograms that are found in any phase
3042        and any phase that uses a histogram.
3043        This also assigns numbers to used phases and histograms by the
3044        order they appear in the file.
3045        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3046        get same info from GPX file.
3047
3048        :returns: (Histograms,Phases)
3049
3050            * Histograms = dictionary of histograms as {name:data,...}
3051            * Phases = dictionary of phases that use histograms
3052        '''
3053        Histograms = {}
3054        Phases = {}
3055        phaseNames = self.GetPhaseNames()
3056        phaseData = self.GetPhaseData()
3057        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3058
3059        for phase in phaseData:
3060            Phase = phaseData[phase]
3061            pId = phaseNames.index(phase)
3062            Phase['pId'] = pId
3063            if Phase['Histograms']:
3064                if phase not in Phases:
3065                    Phases[phase] = Phase
3066                for hist in Phase['Histograms']:
3067                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3068                        Phase['Histograms'][hist]['Use'] = True         
3069                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3070                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3071                        if item:
3072                            if 'PWDR' in hist[:4]: 
3073                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3074                            elif 'HKLF' in hist[:4]:
3075                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3076                            hId = histoList.index(hist)
3077                            Histograms[hist]['hId'] = hId
3078                        else: # would happen if a referenced histogram were renamed or deleted
3079                            print('For phase "'+str(phase)+
3080                                  '" unresolved reference to histogram "'+str(hist)+'"')
3081        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3082        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3083        return Histograms,Phases
3084       
3085    def MakeLSParmDict(self):
3086        '''Load all parameters used for computation from the tree into a
3087        dict of paired values [value, refine flag]. Note that this is
3088        different than the parmDict used in the refinement, which only has
3089        values.
3090
3091        Note that similar things are done in
3092        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3093        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3094        a GPX file).
3095
3096        :returns: (parmDict,varyList) where:
3097
3098         * parmDict is a dict with values and refinement flags
3099           for each parameter and
3100         * varyList is a list of variables (refined parameters).
3101        '''
3102        parmDict = {}
3103        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3104        for phase in Phases:
3105            if 'pId' not in Phases[phase]:
3106                self.ErrorDialog('View parameter error','You must run least squares at least once')
3107                raise Exception,'No pId for phase '+str(phase)
3108        rigidbodyDict = self.PatternTree.GetItemPyData(   
3109            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3110        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3111        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3112        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3113        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3114        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3115        varyList = rbVary+phaseVary+hapVary+histVary
3116        parmDict.update(rbDict)
3117        parmDict.update(phaseDict)
3118        parmDict.update(hapDict)
3119        parmDict.update(histDict)
3120        for parm in parmDict:
3121            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3122                'Omega','Chi','Phi','nDebye','nPeaks']:
3123                parmDict[parm] = [parmDict[parm],'-']
3124            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3125                parmDict[parm] = [parmDict[parm],'-']
3126            elif parm in varyList:
3127                parmDict[parm] = [parmDict[parm],'T']
3128            else:
3129                parmDict[parm] = [parmDict[parm],'F']
3130        # for i in parmDict: print i,'\t',parmDict[i]
3131        # fl = open('parmDict.dat','wb')
3132        # import cPickle
3133        # cPickle.dump(parmDict,fl,1)
3134        # fl.close()
3135        return parmDict,varyList
3136
3137    def ShowLSParms(self,event):
3138        '''Displays a window showing all parameters in the refinement.
3139        Called from the Calculate/View LS Parms menu.
3140        '''
3141        parmDict,varyList = self.MakeLSParmDict()
3142        parmValDict = {}
3143        for i in parmDict:
3144            parmValDict[i] = parmDict[i][0]
3145           
3146        reqVaryList = tuple(varyList) # save requested variables
3147        try:
3148            # process constraints
3149            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3150            Constraints = self.PatternTree.GetItemPyData(sub)
3151            constList = []
3152            for item in Constraints:
3153                if item.startswith('_'): continue
3154                constList += Constraints[item]
3155            G2mv.InitVars()
3156            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3157            groups,parmlist = G2mv.GroupConstraints(constrDict)
3158            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3159            G2mv.Map2Dict(parmValDict,varyList)
3160        except:
3161            pass
3162        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3163        dlg.ShowModal()
3164        dlg.Destroy()
3165       
3166    def OnRefine(self,event):
3167        '''Perform a refinement.
3168        Called from the Calculate/Refine menu.
3169        '''       
3170        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3171        if Id:
3172            dlg = wx.MessageDialog(
3173                self,
3174                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3175                'Remove sequential results?',wx.OK|wx.CANCEL)
3176            if dlg.ShowModal() == wx.ID_OK:
3177                self.PatternTree.Delete(Id)
3178                dlg.Destroy()
3179            else:
3180                dlg.Destroy()
3181                return
3182
3183        self.OnFileSave(event)
3184        # check that constraints are OK here
3185        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3186        if errmsg:
3187            print('Error in constraints:\n'+errmsg+
3188                  '\nRefinement not possible')
3189            self.ErrorDialog('Constraint Error',
3190                             'Error in constraints:\n'+errmsg+
3191                             '\nRefinement not possible')
3192            return
3193        if warnmsg:
3194            print('Conflict between refinment flag settings and constraints:\n'+
3195                  warnmsg+'\nRefinement not possible')
3196            self.ErrorDialog('Refinement Flag Error',
3197                             'Conflict between refinment flag settings and constraints:\n'+
3198                             warnmsg+
3199                             '\nRefinement not possible')
3200            return
3201        #works - but it'd be better if it could restore plots
3202        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3203            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3204        screenSize = wx.ClientDisplayRect()
3205        Size = dlg.GetSize()
3206        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3207        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3208        dlg.SetSize(Size)
3209        Rw = 100.00
3210        try:
3211            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3212        finally:
3213            dlg.Destroy()
3214        oldId =  self.PatternTree.GetSelection()        #retain current selection
3215        oldName = self.PatternTree.GetItemText(oldId)
3216        parentId = self.PatternTree.GetItemParent(oldId)
3217        parentName = ''
3218        if parentId:
3219            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3220        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3221        try:
3222            if dlg.ShowModal() == wx.ID_OK:
3223                Id = 0
3224                self.PatternTree.DeleteChildren(self.root)
3225                if self.HKL: self.HKL = []
3226                if self.G2plotNB.plotList:
3227                    self.G2plotNB.clear()
3228                G2IO.ProjFileOpen(self)
3229                item, cookie = self.PatternTree.GetFirstChild(self.root)
3230                while item and not Id:
3231                    name = self.PatternTree.GetItemText(item)
3232                    if name[:4] in ['PWDR','HKLF']:
3233                        Id = item
3234                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3235                if Id:
3236                    self.PatternTree.SelectItem(Id)
3237                if parentName:
3238                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3239                    if parentId:
3240                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3241                    else:
3242                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3243                    self.PatternTree.SelectItem(itemId)
3244        finally:
3245            dlg.Destroy()
3246
3247    def OnSeqRefine(self,event):
3248        '''Perform a sequential refinement.
3249        Called from the Calculate/Sequential refine menu.
3250        '''       
3251        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3252        if not Id:
3253            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3254            self.PatternTree.SetItemPyData(Id,{})           
3255        self.OnFileSave(event)
3256        # check that constraints are OK here
3257        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3258        if errmsg:
3259            print('Error in constraints:\n'+errmsg+
3260                  '\nRefinement not possible')
3261            self.ErrorDialog('Constraint Error',
3262                             'Error in constraints:\n'+errmsg+
3263                             '\nRefinement not possible')
3264            return
3265        if warnmsg:
3266            print('Conflict between refinment flag settings and constraints:\n'+
3267                  warnmsg+'\nRefinement not possible')
3268            self.ErrorDialog('Refinement Flag Error',
3269                             'Conflict between refinment flag settings and constraints:\n'+
3270                             warnmsg+'\nRefinement not possible')
3271            return
3272        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3273            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3274        screenSize = wx.ClientDisplayRect()
3275        Size = dlg.GetSize()
3276        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3277        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3278        dlg.SetSize(Size)
3279        try:
3280            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3281        finally:
3282            dlg.Destroy()       
3283        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3284        try:
3285            if dlg.ShowModal() == wx.ID_OK:
3286                Id = 0
3287                self.PatternTree.DeleteChildren(self.root)
3288                if self.HKL: self.HKL = []
3289                if self.G2plotNB.plotList:
3290                    self.G2plotNB.clear()
3291                G2IO.ProjFileOpen(self)
3292                Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3293                self.PatternTree.SelectItem(Id)
3294
3295        finally:
3296            dlg.Destroy()
3297       
3298    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3299        'Display an error message'
3300        result = None
3301        if parent is None:
3302            dlg = wx.MessageDialog(self, message, title,  wtype)
3303        else:
3304            dlg = wx.MessageDialog(parent, message, title,  wtype)
3305            dlg.CenterOnParent() # not working on Mac
3306        try:
3307            result = dlg.ShowModal()
3308        finally:
3309            dlg.Destroy()
3310        return result
3311
3312class GSASIImain(wx.App):
3313    '''Defines a wxApp for GSAS-II
3314
3315    Creates a wx frame (self.main) which contains the display of the
3316    data tree.
3317    '''
3318    def OnInit(self):
3319        '''Called automatically when the app is created.'''
3320        if '2.7' not in sys.version[:5]:
3321            dlg = wx.MessageDialog(None, 
3322                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version[:5],
3323                'Python version error',  wx.OK)
3324            try:
3325                result = dlg.ShowModal()
3326            finally:
3327                dlg.Destroy()
3328            sys.exit()
3329        self.main = GSASII(None)
3330        self.main.Show()
3331        self.SetTopWindow(self.main)
3332        # DEBUG: jump to sequential results
3333        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3334        #self.main.PatternTree.SelectItem(Id)
3335        # end DEBUG
3336        return True
3337    # def MacOpenFile(self, filename):
3338    #     '''Called on Mac every time a file is dropped on the app when it is running,
3339    #     treat this like a File/Open project menu action.
3340    #     Should be ignored on other platforms
3341    #     '''
3342    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3343    #     print 'MacOpen',filename
3344    #     if os.path.splitext(filename)[1] == '.py': return
3345    #     # end PATCH
3346    #     self.main.OnFileOpen(None,filename)
3347    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3348   
3349def main():
3350    '''Start up the GSAS-II application'''
3351    #application = GSASIImain() # don't redirect output, someday we
3352    # may want to do this if we can
3353    application = GSASIImain(0)
3354    if wxInspector:
3355        import wx.lib.inspection as wxeye
3356        wxeye.InspectionTool().Show()
3357
3358    #application.main.OnRefine(None)
3359    application.MainLoop()
3360   
3361if __name__ == '__main__':
3362    # print versions
3363    print "Python module versions loaded:"
3364    print "python:     ",sys.version[:5]
3365    print "wxpython:   ",wx.__version__
3366    print "matplotlib: ",mpl.__version__
3367    print "numpy:      ",np.__version__
3368    print "scipy:      ",sp.__version__
3369    print "OpenGL:     ",ogl.__version__
3370    try:
3371        from PIL import Image
3372        try:
3373            from PIL import PILLOW_VERSION
3374            version = PILLOW_VERSION
3375        except:
3376            version = Image.VERSION
3377        print "pillow:     ",version
3378    except ImportError:
3379        try:
3380            import Image
3381            print "Image (PIL):",Image.VERSION
3382        except ImportError:
3383            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3384    try:
3385        import mkl
3386        print "Max threads ",mkl.get_max_threads()
3387    except:
3388        pass
3389    import platform
3390    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3391    #print "wxPython description",wx.PlatformInfo
3392    print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
3393    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.