source: trunk/GSASII.py @ 1359

Last change on this file since 1359 was 1359, checked in by vondreele, 8 years ago

start allowing reading of multiple HKLF files.
put an IndexAllIds? into UpdateConstraints?
edf image fixes
more places in Pawley d-min
format of penalty fxn chi2
allow one Bank selection set to be used for multiple files
new reader for TOF HKLF2 data - nonworking just now

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