source: trunk/GSASII.py @ 1355

Last change on this file since 1355 was 1355, checked in by toby, 9 years ago

initial 10.9/Canopy fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 155.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2014-05-20 16:49:19 +0000 (Tue, 20 May 2014) $
6# $Author: toby $
7# $Revision: 1355 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1355 2014-05-20 16:49:19Z toby $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. Please restart GSAS-II')
59    print('*******************************************************')         
60    sys.exit()
61   
62# load the GSAS routines
63import GSASIIpath
64GSASIIpath.SetVersionNumber("$Revision: 1355 $")
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')
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                        for i in range(8):
1025                            s = Iparm['INS  1IECOR'+str(i+1)].split()
1026                            Icovar += [float(S) for S in s]
1027                        Inst2['Icoeff'] = Icoeff
1028                        Inst2['Iesd'] = Iesd
1029                        Inst2['Icovar'] = Icovar
1030                return [Inst1,Inst2]
1031
1032        # stuff we might need from the reader
1033        filename = rd.powderentry[0]
1034        bank = rd.powderentry[2]
1035        numbanks = rd.numbanks
1036        # is there an instrument parameter file defined for the current data set?
1037        # or if this is a read on a set of set of files, use the last one again
1038        #if rd.instparm or (lastdatafile == filename and lastIparmfile):
1039        if rd.instparm or lastIparmfile:
1040            if rd.instparm:
1041                instfile = os.path.join(os.path.split(filename)[0],
1042                                    rd.instparm)
1043            else:
1044                # for multiple reads of one data file, reuse the inst parm file
1045                instfile = lastIparmfile
1046            if os.path.exists(instfile):
1047                #print 'debug: try read',instfile
1048                instParmList = self.ReadPowderInstprm(instfile)
1049                if instParmList is not None:
1050                    rd.instfile = instfile
1051                    rd.instmsg = 'GSAS-II file '+instfile
1052                    return instParmList
1053                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1054                if Iparm:
1055                    #print 'debug: success'
1056                    rd.instfile = instfile
1057                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1058                    return SetPowderInstParms(Iparm,rd)
1059            else:
1060                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1061                    +str(instfile)+' requested by file '+ filename)
1062        # is there an instrument parameter file matching the current file
1063        # with extension .inst or .prm? If so read it
1064        basename = os.path.splitext(filename)[0]
1065        for ext in '.instprm','.prm','.inst','.ins':
1066            instfile = basename + ext
1067            instParmList = self.ReadPowderInstprm(instfile)
1068            if instParmList is not None:
1069                rd.instfile = instfile
1070                rd.instmsg = 'GSAS-II file '+instfile
1071                return instParmList
1072            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1073            if Iparm:
1074                #print 'debug: success'
1075                rd.instfile = instfile
1076                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1077                return SetPowderInstParms(Iparm,rd)
1078            else:
1079                #print 'debug: open/read failed',instfile
1080                pass # fail silently
1081
1082        # did we read the data file from a zip? If so, look there for a
1083        # instrument parameter file
1084        if self.zipfile:
1085            for ext in '.instprm','.prm','.inst','.ins':
1086                instfile = G2IO.ExtractFileFromZip(
1087                    self.zipfile,
1088                    selection=os.path.split(basename + ext)[1],
1089                    parent=self)
1090                if instfile is not None and instfile != self.zipfile:
1091                    print 'debug:',instfile,'created from ',self.zipfile
1092                    instParmList = self.ReadPowderInstprm(instfile)
1093                    if instParmList is not None:
1094                        rd.instfile = instfile
1095                        rd.instmsg = 'GSAS-II file '+instfile
1096                        return instParmList
1097                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1098                    if Iparm:
1099                        rd.instfile = instfile
1100                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1101                        return SetPowderInstParms(Iparm,rd)
1102                    else:
1103                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
1104                        pass # fail silently
1105
1106        while True: # loop until we get a file that works or we get a cancel
1107            instfile = ''
1108            dlg = wx.FileDialog(
1109                self,
1110                'Choose inst. param file for "'
1111                +rd.idstring
1112                +'" (or Cancel for default)',
1113                '.', '',
1114                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1115                'GSAS-II iparm file (*.instprm)|*.instprm|'
1116                'All files (*.*)|*.*', 
1117                wx.OPEN|wx.CHANGE_DIR)
1118            if os.path.exists(lastIparmfile):
1119                dlg.SetFilename(lastIparmfile)
1120            if dlg.ShowModal() == wx.ID_OK:
1121                instfile = dlg.GetPath()
1122            dlg.Destroy()
1123            if not instfile: break
1124            instParmList = self.ReadPowderInstprm(instfile)
1125            if instParmList is not None:
1126                rd.instfile = instfile
1127                rd.instmsg = 'GSAS-II file '+instfile
1128                return instParmList
1129            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1130            if Iparm:
1131                #print 'debug: success with',instfile
1132                rd.instfile = instfile
1133                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1134                return SetPowderInstParms(Iparm,rd)
1135            else:
1136                self.ErrorDialog('Read Error',
1137                                 'Error opening/reading file '+str(instfile))
1138       
1139        # still no success: offer user choice of defaults
1140        while True: # loop until we get a choice
1141            choices = []
1142            head = 'Select from default instrument parameters for '+rd.idstring
1143
1144            for l in rd.defaultIparm_lbl:
1145                choices.append('Defaults for '+l)
1146            res = rd.BlockSelector(
1147                choices,
1148                ParentFrame=self,
1149                title=head,
1150                header='Select default inst parms',
1151                useCancel=False)
1152            if res is None: continue
1153            rd.instfile = ''
1154            rd.instmsg = 'default: '+rd.defaultIparm_lbl[res]
1155            return SetPowderInstParms(rd.defaultIparms[res],rd)
1156
1157    def OnImportPowder(self,event):
1158        '''Called in response to an Import/Powder Data/... menu item
1159        to read a powder diffraction data set.
1160        dict self.ImportMenuId is used to look up the specific
1161        reader item associated with the menu item, which will be
1162        None for the last menu item, which is the "guess" option
1163        where all appropriate formats will be tried.
1164
1165        Also reads an instrument parameter file for each dataset.
1166        '''
1167        # get a list of existing histograms
1168        PWDRlist = []
1169        if self.PatternTree.GetCount():
1170            item, cookie = self.PatternTree.GetFirstChild(self.root)
1171            while item:
1172                name = self.PatternTree.GetItemText(item)
1173                if name.startswith('PWDR ') and name not in PWDRlist:
1174                    PWDRlist.append(name)
1175                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1176        # look up which format was requested
1177        reqrdr = self.ImportMenuId.get(event.GetId()) 
1178        rdlist = self.OnImportGeneric(
1179            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1180        if len(rdlist) == 0: return
1181        self.CheckNotebook()
1182        Iparm = None
1183        lastIparmfile = ''
1184        lastdatafile = ''
1185        newHistList = []
1186        self.EnablePlot = False
1187        for rd in rdlist:
1188            # get instrument parameters for each dataset
1189            Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1190            if rd.repeat_instparm: 
1191                lastIparmfile = rd.instfile
1192            lastdatafile = rd.powderentry[0]
1193            HistName = rd.idstring
1194            HistName = 'PWDR '+HistName
1195            # make new histogram names unique
1196            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1197            print 'Read powder data '+str(HistName)+ \
1198                ' from file '+str(rd.readfilename) + \
1199                ' with parameters from '+str(rd.instmsg)
1200            # data are read, now store them in the tree
1201            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1202            if 'T' in Iparm1['Type'][0]:
1203                if not rd.clockWd and rd.GSAS:
1204                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1205                cw = np.diff(rd.powderdata[0])
1206                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1207                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1208                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1209                if 'Itype' in Iparm2:
1210                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1211                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1212                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1213                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1214                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1215                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1216                    var += WYI*rd.powderdata[1]**2
1217                    var /= YI**2
1218                    rd.powderdata[2] = 1./var
1219                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1220                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1221                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1222            Tmin = min(rd.powderdata[0])
1223            Tmax = max(rd.powderdata[0])
1224            valuesdict = {
1225                'wtFactor':1.0,
1226                'Dummy':False,
1227                'ranId':ran.randint(0,sys.maxint),
1228                }
1229            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1230            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1231            self.PatternTree.SetItemPyData(
1232                self.PatternTree.AppendItem(Id,text='Comments'),
1233                rd.comments)
1234            self.PatternTree.SetItemPyData(
1235                self.PatternTree.AppendItem(Id,text='Limits'),
1236                [(Tmin,Tmax),[Tmin,Tmax]])
1237            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1238            self.PatternTree.SetItemPyData(
1239                self.PatternTree.AppendItem(Id,text='Background'),
1240                [['chebyschev',True,3,1.0,0.0,0.0],
1241                 {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1242            self.PatternTree.SetItemPyData(
1243                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1244                [Iparm1,Iparm2])
1245            self.PatternTree.SetItemPyData(
1246                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1247                rd.Sample)
1248            self.PatternTree.SetItemPyData(
1249                self.PatternTree.AppendItem(Id,text='Peak List')
1250                ,[])
1251            self.PatternTree.SetItemPyData(
1252                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1253                [])
1254            self.PatternTree.SetItemPyData(
1255                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1256                [])
1257            self.PatternTree.SetItemPyData(
1258                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1259                {})
1260            newHistList.append(HistName)
1261        else:
1262            self.EnablePlot = True
1263            self.PatternTree.Expand(Id)
1264            self.PatternTree.SelectItem(Id)
1265           
1266        if not newHistList: return # somehow, no new histograms
1267        # make a list of phase names
1268        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1269        phaseNameList = usedHistograms.keys() # phase names in use
1270        if not phaseNameList: return # no phases yet, nothing to do
1271        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1272        for Name in newHistList:
1273            header += '\n  '+str(Name)
1274
1275        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1276        if not result: return
1277        # connect new phases to histograms
1278        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1279        if not sub:
1280            raise Exception('ERROR -- why are there no phases here?')
1281        item, cookie = self.PatternTree.GetFirstChild(sub)
1282        iph = -1
1283        while item: # loop over (new) phases
1284            iph += 1
1285            phaseName = self.PatternTree.GetItemText(item)
1286            data = self.PatternTree.GetItemPyData(item)
1287            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1288            if iph not in result: continue
1289            generalData = data['General']
1290            SGData = generalData['SGData']
1291            UseList = data['Histograms']
1292            NShkl = len(G2spc.MustrainNames(SGData))
1293            NDij = len(G2spc.HStrainNames(SGData))
1294            for histoName in newHistList:
1295                UseList[histoName] = {
1296                    'Histogram':histoName,'Show':False,
1297                    'Scale':[1.0,False],'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{}],
1298                    'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
1299                            [1.,1.,1.,0.,0.,0.],6*[False,]],
1300                    'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
1301                                NShkl*[0.01,],NShkl*[False,]],
1302                    'HStrain':[NDij*[0.0,],NDij*[False,]],                         
1303                    'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
1304                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1305                refList = self.PatternTree.GetItemPyData(
1306                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1307                refList[generalData['Name']] = []
1308        return # success
1309
1310    def OnDummyPowder(self,event):
1311        '''Called in response to Import/Powder Data/Simulate menu item
1312        to create a Dummy powder diffraction data set.
1313
1314        Reads an instrument parameter file and then gets input from the user
1315        '''
1316        # get a list of existing histograms
1317        PWDRlist = []
1318        if self.PatternTree.GetCount():
1319            item, cookie = self.PatternTree.GetFirstChild(self.root)
1320            while item:
1321                name = self.PatternTree.GetItemText(item)
1322                if name.startswith('PWDR ') and name not in PWDRlist:
1323                    PWDRlist.append(name)
1324                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1325        # Initialize a base class reader
1326        rd = G2IO.ImportPowderData(
1327            extensionlist=tuple(),
1328            strictExtension=False,
1329            formatName = 'Simulate dataset',
1330            longFormatName = 'Compute a simulated pattern')
1331        rd.powderentry[0] = '' # no filename
1332        # #self.powderentry[1] = pos # bank offset (N/A here)
1333        rd.powderentry[2] = 1 # only one bank
1334        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1335        self.CheckNotebook()
1336        Iparm = None
1337        lastIparmfile = ''
1338        lastdatafile = ''
1339        self.zipfile = None
1340        # get instrument parameters for it
1341        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1342        if 'T' in Iparm1['Type'][0]:
1343            print('TOF simulation not supported yet')
1344            return False
1345        else:
1346            # need to get name, 2theta start, end, step
1347            rd.idstring = ' CW'
1348            if 'X' in Iparm1['Type'][0]:
1349                rd.idstring = 'CW x-ray simulation'
1350            else:
1351                rd.idstring = 'CW neutron simulation'
1352            # base initial range on wavelength
1353            wave = Iparm1.get('Lam')
1354            if wave:
1355                wave = wave[0]
1356            else:
1357                wave = Iparm1.get('Lam1')
1358                if wave:
1359                    wave = wave[0]
1360        N = 0
1361        while (N < 3): # insist on a dataset with a few points
1362            names = ('dataset name', 'start angle', 'end angle', 'step size')
1363            if not wave or wave < 1.0:
1364                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1365            else:
1366                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1367            dlg = G2gd.ScrolledMultiEditor(
1368                self,[inp] * len(inp),range(len(inp)),names,
1369                header='Enter simulation name and range',
1370                minvals=(None,0.001,0.001,0.0001),
1371                maxvals=(None,180.,180.,.1),
1372                sizevals=((225,-1),)
1373                )
1374            dlg.CenterOnParent()
1375            if dlg.ShowModal() == wx.ID_OK:
1376                if inp[1] > inp[2]:
1377                    end,start,step = inp[1:]
1378                else:               
1379                    start,end,step = inp[1:]
1380                step = abs(step)
1381            else:
1382                return False
1383            N = int((end-start)/step)+1
1384            x = np.linspace(start,end,N,True)
1385            N = len(x)
1386        rd.powderdata = [
1387            np.array(x), # x-axis values
1388            np.zeros_like(x), # powder pattern intensities
1389            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1390            np.zeros_like(x), # calc. intensities (zero)
1391            np.zeros_like(x), # calc. background (zero)
1392            np.zeros_like(x), # obs-calc profiles
1393            ]
1394        Tmin = rd.powderdata[0][0]
1395        Tmax = rd.powderdata[0][-1]
1396        # data are read, now store them in the tree
1397        HistName = inp[0]
1398        HistName = 'PWDR '+HistName
1399        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1400        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1401        valuesdict = {
1402            'wtFactor':1.0,
1403            'Dummy':True,
1404            'ranId':ran.randint(0,sys.maxint),
1405            }
1406        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1407        self.PatternTree.SetItemPyData(
1408            self.PatternTree.AppendItem(Id,text='Comments'),
1409            rd.comments)
1410        self.PatternTree.SetItemPyData(
1411            self.PatternTree.AppendItem(Id,text='Limits'),
1412            [(Tmin,Tmax),[Tmin,Tmax]])
1413        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1414        self.PatternTree.SetItemPyData(
1415            self.PatternTree.AppendItem(Id,text='Background'),
1416            [['chebyschev',True,3,1.0,0.0,0.0],
1417             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1418        self.PatternTree.SetItemPyData(
1419            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1420            [Iparm1,Iparm2])
1421        self.PatternTree.SetItemPyData(
1422            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1423            rd.Sample)
1424        self.PatternTree.SetItemPyData(
1425            self.PatternTree.AppendItem(Id,text='Peak List')
1426            ,[])
1427        self.PatternTree.SetItemPyData(
1428            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1429            [])
1430        self.PatternTree.SetItemPyData(
1431            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1432            [])
1433        self.PatternTree.SetItemPyData(
1434            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1435            {})
1436        self.PatternTree.Expand(Id)
1437        self.PatternTree.SelectItem(Id)
1438        print('Added simulation powder data '+str(HistName)+ 
1439              ' with parameters from '+str(rd.instmsg))
1440
1441        # make a list of phase names
1442        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1443        phaseNameList = usedHistograms.keys() # phase names in use
1444        if not phaseNameList: return # no phases yet, nothing to do
1445        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1446        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1447        if not result: return
1448        # connect new phases to histograms
1449        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1450        if not sub:
1451            raise Exception('ERROR -- why are there no phases here?')
1452        item, cookie = self.PatternTree.GetFirstChild(sub)
1453        iph = -1
1454        while item: # loop over (new) phases
1455            iph += 1
1456            phaseName = self.PatternTree.GetItemText(item)
1457            data = self.PatternTree.GetItemPyData(item)
1458            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1459            if iph not in result: continue
1460            generalData = data['General']
1461            SGData = generalData['SGData']
1462            UseList = data['Histograms']
1463            NShkl = len(G2spc.MustrainNames(SGData))
1464            NDij = len(G2spc.HStrainNames(SGData))
1465            UseList[HistName] = {
1466                'Histogram':HistName,'Show':False,
1467                'Scale':[1.0,False],'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{}],
1468                'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
1469                        [1.,1.,1.,0.,0.,0.],6*[False,]],
1470                'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
1471                            NShkl*[0.01,],NShkl*[False,]],
1472                'HStrain':[NDij*[0.0,],NDij*[False,]],                         
1473                'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
1474            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1475            refList = self.PatternTree.GetItemPyData(
1476                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1477            refList[generalData['Name']] = []
1478        return # success
1479
1480    def _Add_ImportMenu_smallangle(self,parent):
1481        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1482        '''
1483        submenu = wx.Menu()
1484        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1485            submenu, help='Import small angle data')
1486        for reader in self.ImportSmallAngleReaderlist:
1487            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1488                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1489            self.ImportMenuId[item.GetId()] = reader
1490            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1491        # item = submenu.Append(wx.ID_ANY,
1492        #     help='Import small angle data, use file to try to determine format',
1493        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1494        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1495
1496    def OnImportSmallAngle(self,event):
1497        '''Called in response to an Import/Small Angle Data/... menu item
1498        to read a small angle diffraction data set.
1499        dict self.ImportMenuId is used to look up the specific
1500        reader item associated with the menu item, which will be
1501        None for the last menu item, which is the "guess" option
1502        where all appropriate formats will be tried.
1503
1504        '''
1505       
1506        def GetSASDIparm(reader):
1507            parm = reader.instdict
1508            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1509                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1510            return Iparm,{}
1511           
1512        # get a list of existing histograms
1513        SASDlist = []
1514        if self.PatternTree.GetCount():
1515            item, cookie = self.PatternTree.GetFirstChild(self.root)
1516            while item:
1517                name = self.PatternTree.GetItemText(item)
1518                if name.startswith('SASD ') and name not in SASDlist:
1519                    SASDlist.append(name)
1520                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1521        # look up which format was requested
1522        reqrdr = self.ImportMenuId.get(event.GetId()) 
1523        rdlist = self.OnImportGeneric(
1524            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1525        if len(rdlist) == 0: return
1526        self.CheckNotebook()
1527        Iparm = None
1528        lastdatafile = ''
1529        newHistList = []
1530        self.EnablePlot = False
1531        for rd in rdlist:
1532            lastdatafile = rd.smallangleentry[0]
1533            HistName = rd.idstring
1534            HistName = 'SASD '+HistName
1535            # make new histogram names unique
1536            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1537            print 'Read small angle data '+str(HistName)+ \
1538                ' from file '+str(self.lastimport)
1539            # data are read, now store them in the tree
1540            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1541            Iparm1,Iparm2 = GetSASDIparm(rd)
1542#            if 'T' in Iparm1['Type'][0]:
1543#                if not rd.clockWd and rd.GSAS:
1544#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1545#                cw = np.diff(rd.powderdata[0])
1546#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1547#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1548#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1549#                if 'Itype' in Iparm2:
1550#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1551#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1552#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1553#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1554#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1555#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1556#                    var += WYI*rd.powderdata[1]**2
1557#                    var /= YI**2
1558#                    rd.powderdata[2] = 1./var
1559#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1560#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1561#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1562            Tmin = min(rd.smallangledata[0])
1563            Tmax = max(rd.smallangledata[0])
1564            valuesdict = {
1565                'wtFactor':1.0,
1566                'Dummy':False,
1567                'ranId':ran.randint(0,sys.maxint),
1568                }
1569            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1570            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1571            self.PatternTree.SetItemPyData(
1572                self.PatternTree.AppendItem(Id,text='Comments'),
1573                rd.comments)
1574            self.PatternTree.SetItemPyData(
1575                self.PatternTree.AppendItem(Id,text='Limits'),
1576                [(Tmin,Tmax),[Tmin,Tmax]])
1577            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1578            self.PatternTree.SetItemPyData(
1579                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1580                [Iparm1,Iparm2])
1581            self.PatternTree.SetItemPyData(
1582                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1583            self.PatternTree.SetItemPyData(
1584                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1585                rd.Sample)
1586            self.PatternTree.SetItemPyData(
1587                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1588            newHistList.append(HistName)
1589        else:
1590            self.EnablePlot = True
1591            self.PatternTree.Expand(Id)
1592            self.PatternTree.SelectItem(Id)
1593           
1594        if not newHistList: return # somehow, no new histograms
1595        return # success
1596
1597    def _init_Exports(self,menu):
1598        '''Find exporter routines and add them into menus
1599        '''
1600        # set up the top-level menus
1601        projectmenu = wx.Menu()
1602        item = menu.AppendMenu(
1603            wx.ID_ANY, 'Entire project as',
1604            projectmenu, help='Export entire project')
1605
1606        phasemenu = wx.Menu()
1607        item = menu.AppendMenu(
1608            wx.ID_ANY, 'Phase as',
1609            phasemenu, help='Export phase or sometimes phases')
1610
1611        powdermenu = wx.Menu()
1612        item = menu.AppendMenu(
1613            wx.ID_ANY, 'Powder data as',
1614            powdermenu, help='Export powder diffraction histogram(s)')
1615
1616        singlemenu = wx.Menu()
1617        item = menu.AppendMenu(
1618            wx.ID_ANY, 'Single crystal data as',
1619            singlemenu, help='Export single crystal histogram(s)')
1620
1621        imagemenu = wx.Menu()
1622        item = menu.AppendMenu(
1623            wx.ID_ANY, 'Image data as',
1624            imagemenu, help='Export powder image(s) data')
1625
1626        mapmenu = wx.Menu()
1627        item = menu.AppendMenu(
1628            wx.ID_ANY, 'Maps as',
1629            mapmenu, help='Export density map(s)')
1630
1631        # pdfmenu = wx.Menu()
1632        # item = menu.AppendMenu(
1633        #     wx.ID_ANY, 'PDFs as',
1634        #     pdfmenu, help='Export pair distribution function(s)')
1635
1636        # find all the exporter files
1637        pathlist = sys.path
1638        filelist = []
1639        for path in pathlist:
1640            for filename in glob.iglob(os.path.join(
1641                path,
1642                "G2export*.py")):
1643                filelist.append(filename)   
1644        filelist = sorted(list(set(filelist))) # remove duplicates
1645        exporterlist = []
1646        # go through the routines and import them, saving objects that
1647        # have export routines (method Exporter)
1648        for filename in filelist:
1649            path,rootname = os.path.split(filename)
1650            pkg = os.path.splitext(rootname)[0]
1651            try:
1652                fp = None
1653                fp, fppath,desc = imp.find_module(pkg,[path,])
1654                pkg = imp.load_module(pkg,fp,fppath,desc)
1655                for clss in inspect.getmembers(pkg): # find classes defined in package
1656                    if clss[0].startswith('_'): continue
1657                    if inspect.isclass(clss[1]):
1658                        # check if we have the required methods
1659                        for m in 'Exporter','loadParmDict':
1660                            if not hasattr(clss[1],m): break
1661                            if not callable(getattr(clss[1],m)): break
1662                        else:
1663                            exporter = clss[1](self) # create an export instance
1664                            exporterlist.append(exporter)
1665            except AttributeError:
1666                print 'Import_'+errprefix+': Attribute Error'+str(filename)
1667                pass
1668            except ImportError:
1669                print 'Import_'+errprefix+': Error importing file'+str(filename)
1670                pass
1671            if fp: fp.close()
1672        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
1673        for obj in exporterlist:
1674            #print 'exporter',obj
1675            for typ in obj.exporttype:
1676                if typ == "project":
1677                    submenu = projectmenu
1678                elif typ == "phase":
1679                    submenu = phasemenu
1680                elif typ == "powder":
1681                    submenu = powdermenu
1682                elif typ == "single":
1683                    submenu = singlemenu
1684                elif typ == "image":
1685                    submenu = imagemenu
1686                elif typ == "map":
1687                    submenu = mapmenu
1688                # elif typ == "pdf":
1689                #     submenu = pdfmenu
1690                else:
1691                    print("Error, unknown type in "+str(obj))
1692                    break
1693                item = submenu.Append(
1694                    wx.ID_ANY,
1695                    help=obj.longFormatName,
1696                    kind=wx.ITEM_NORMAL,
1697                    text=obj.formatName)
1698                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
1699                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
1700               
1701        #code to debug an Exporter. hard-coded the routine below, to allow a reload before use
1702        # def DebugExport(event):
1703        #      print 'start reload'
1704        #      reload(G2IO)
1705        #      import G2export_pwdr as dev
1706        #      reload(dev)
1707        #      dev.ExportPowderFXYE(self).Exporter(event)
1708        # item = menu.Append(
1709        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1710        #     help="debug exporter",text="test Export FXYE")
1711        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
1712        # # #self.ExportLookup[item.GetId()] = 'image'
1713        # self.ExportLookup[item.GetId()] = 'powder'
1714
1715    def _Add_ExportMenuItems(self,parent):
1716        # item = parent.Append(
1717        #     help='Select PWDR item to enable',id=wx.ID_ANY,
1718        #     kind=wx.ITEM_NORMAL,
1719        #     text='Export Powder Patterns...')
1720        # self.ExportPattern.append(item)
1721        # item.Enable(False)
1722        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1723
1724        item = parent.Append(
1725            help='',id=wx.ID_ANY,
1726            kind=wx.ITEM_NORMAL,
1727            text='Export All Peak Lists...')
1728        self.ExportPeakList.append(item)
1729        item.Enable(True)
1730        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1731
1732        item = parent.Append(
1733            help='',id=wx.ID_ANY,
1734            kind=wx.ITEM_NORMAL,
1735            text='Export HKLs...')
1736        self.ExportHKL.append(item)
1737        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1738
1739        item = parent.Append(
1740            help='Select PDF item to enable',
1741            id=wx.ID_ANY,
1742            kind=wx.ITEM_NORMAL,
1743            text='Export PDF...')
1744        self.ExportPDF.append(item)
1745        item.Enable(False)
1746        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1747
1748    def FillMainMenu(self,menubar):
1749        '''Define contents of the main GSAS-II menu for the (main) data tree window
1750        in the mac, used also for the data item windows as well.
1751        '''
1752        File = wx.Menu(title='')
1753        menubar.Append(menu=File, title='&File')
1754        self._Add_FileMenuItems(File)
1755        Data = wx.Menu(title='')
1756        menubar.Append(menu=Data, title='Data')
1757        self._Add_DataMenuItems(Data)
1758        Calculate = wx.Menu(title='')       
1759        menubar.Append(menu=Calculate, title='&Calculate')
1760        self._Add_CalculateMenuItems(Calculate)
1761        Import = wx.Menu(title='')       
1762        menubar.Append(menu=Import, title='Import')
1763        self._Add_ImportMenu_Phase(Import)
1764        self._Add_ImportMenu_powder(Import)
1765        self._Add_ImportMenu_Sfact(Import)
1766        self._Add_ImportMenu_smallangle(Import)
1767        #======================================================================
1768        # Code to help develop/debug an importer, much is hard-coded below
1769        # but module is reloaded before each use, allowing faster testing
1770        # def DebugImport(event):
1771        #     print 'start reload'
1772        #     import G2phase_ISO as dev
1773        #     reload(dev)
1774        #     rd = dev.ISODISTORTPhaseReader()
1775        #     self.ImportMenuId[event.GetId()] = rd
1776        #     self.OnImportPhase(event)
1777            # or ----------------------------------------------------------------------
1778            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
1779            # special debug code
1780            # or ----------------------------------------------------------------------
1781            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
1782            # fp = open(filename,'Ur')
1783            # if not rd.ContentsValidator(fp):
1784            #     print 'not validated'
1785            #     # make a list of used phase ranId's
1786            # phaseRIdList = []
1787            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1788            # if sub:
1789            #     item, cookie = self.PatternTree.GetFirstChild(sub)
1790            #     while item:
1791            #         phaseName = self.PatternTree.GetItemText(item)
1792            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
1793            #         if ranId: phaseRIdList.append(ranId)
1794            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1795            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
1796            #     print 'read OK'
1797        # item = Import.Append(
1798        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1799        #     help="debug importer",text="test importer")
1800        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
1801        #======================================================================
1802        self.ExportMenu = wx.Menu(title='')
1803        menubar.Append(menu=self.ExportMenu, title='Export')
1804        self._init_Exports(self.ExportMenu)
1805        self._Add_ExportMenuItems(self.ExportMenu)
1806        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1807            morehelpitems=[('&Tutorials','Tutorials')])
1808        menubar.Append(menu=HelpMenu,title='&Help')
1809
1810    def _init_ctrls(self, parent):
1811        wx.Frame.__init__(self, name='GSASII', parent=parent,
1812            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1813        clientSize = wx.ClientDisplayRect()
1814        Size = self.GetSize()
1815        xPos = clientSize[2]-Size[0]
1816        self.SetPosition(wx.Point(xPos,clientSize[1]))
1817        self._init_Imports()
1818        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1819        self.MakePDF = []
1820        self.Refine = []
1821        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
1822        #self.ExportPattern = []
1823        self.ExportPeakList = []
1824        self.ExportHKL = []
1825        self.ExportPDF = []
1826        self.ExportPhase = []
1827        self.ExportCIF = []
1828        #
1829        self.GSASIIMenu = wx.MenuBar()
1830        self.FillMainMenu(self.GSASIIMenu)
1831        self.SetMenuBar(self.GSASIIMenu)
1832        self.Bind(wx.EVT_SIZE, self.OnSize)
1833        self.CreateStatusBar()
1834        self.mainPanel = wx.Panel(self,-1)
1835       
1836        wxID_PATTERNTREE = wx.NewId()
1837        self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE,
1838            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1839        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,
1840            self.OnPatternTreeSelChanged, id=wxID_PATTERNTREE)
1841        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1842            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1843        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1844            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1845        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1846            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1847        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1848            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1849        self.root = self.PatternTree.AddRoot('Loaded Data: ')
1850       
1851        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1852            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1853        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1854        plotFrame.Show()
1855       
1856        self.dataDisplay = None
1857       
1858    def __init__(self, parent):
1859        self.ExportLookup = {}
1860        self._init_ctrls(parent)
1861        self.Image = wx.Image(
1862            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
1863            wx.BITMAP_TYPE_ICO)
1864        if "wxMSW" in wx.PlatformInfo:
1865            img = self.Image.Scale(16, 16).ConvertToBitmap()
1866        elif "wxGTK" in wx.PlatformInfo:
1867            img = self.Image.Scale(22, 22).ConvertToBitmap()
1868        else:
1869            img = self.Image.ConvertToBitmap()
1870        self.SetIcon(wx.IconFromBitmap(img))
1871        self.Bind(wx.EVT_CLOSE, self.ExitMain)
1872        # various defaults
1873        self.oldFocus = None
1874        self.GSASprojectfile = ''
1875        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
1876        self.exportDir = None  # the last directory used for exports, if any.
1877        self.undofile = ''
1878        self.TreeItemDelete = False
1879        self.Offset = [0.0,0.0]
1880        self.delOffset = .02
1881        self.refOffset = -100.0
1882        self.refDelt = .01
1883        self.Weight = False
1884        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
1885        self.IfPlot = False
1886        self.PatternId = 0
1887        self.PickId = 0
1888        self.PeakTable = []
1889        self.LimitsTable = []
1890        self.HKL = []
1891        self.Lines = []
1892        self.itemPicked = None
1893        self.dataFrame = None
1894        self.Interpolate = 'nearest'
1895        self.ContourColor = 'Paired'
1896        self.VcovColor = 'RdYlGn'
1897        self.RamaColor = 'Blues'
1898        self.Projection = 'equal area'
1899        self.logPlot = False
1900        self.qPlot = False
1901        self.sqPlot = False
1902        self.ErrorBars = False
1903        self.Contour = False
1904        self.Legend = False
1905        self.SinglePlot = True
1906        self.SubBack = False
1907        self.seqReverse = False
1908        self.plotView = 0
1909        self.Image = 0
1910        self.oldImagefile = ''
1911        self.ImageZ = []
1912        self.Integrate = 0
1913        self.imageDefault = {}
1914        self.Sngl = False
1915        self.ifGetRing = False
1916        self.MaskKey = ''           #trigger for making image masks
1917        self.StrainKey = ''         #ditto for new strain d-zeros
1918        self.EnablePlot = True
1919        self.Tutorials = False      #used for changing default directory
1920        arg = sys.argv
1921        if len(arg) > 1:
1922            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1923            self.dirname = os.path.dirname(arg[1])
1924            if self.dirname: os.chdir(self.dirname)
1925            try:
1926                G2IO.ProjFileOpen(self)
1927                self.PatternTree.Expand(self.root)
1928                for item in self.Refine: item.Enable(True)
1929                self.EnableSeqRefineMenu()
1930
1931            except:
1932                print 'Error opening file',arg[1]
1933
1934    def OnSize(self,event):
1935        'Called when the main window is resized. Not sure why'
1936        w,h = self.GetClientSizeTuple()
1937        self.mainPanel.SetSize(wx.Size(w,h))
1938        self.PatternTree.SetSize(wx.Size(w,h))
1939                       
1940    def OnPatternTreeSelChanged(self, event):
1941        '''Called when a data tree item is selected'''
1942        if self.TreeItemDelete:
1943            self.TreeItemDelete = False
1944        else:
1945            pltNum = self.G2plotNB.nb.GetSelection()
1946            if pltNum >= 0:                         #to avoid the startup with no plot!
1947                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1948                pltPlot = pltPage.figure
1949            item = event.GetItem()
1950            G2gd.MovePatternTreeToGrid(self,item)
1951            if self.oldFocus:
1952                self.oldFocus.SetFocus()
1953       
1954    def OnPatternTreeItemCollapsed(self, event):
1955        'Called when a tree item is collapsed'
1956        event.Skip()
1957
1958    def OnPatternTreeItemExpanded(self, event):
1959        'Called when a tree item is expanded'
1960        event.Skip()
1961       
1962    def OnPatternTreeItemDelete(self, event):
1963        'Called when a tree item is deleted -- not sure what this does'
1964        self.TreeItemDelete = True
1965
1966    def OnPatternTreeItemActivated(self, event):
1967        'Called when a tree item is activated'
1968        event.Skip()
1969       
1970    def OnPatternTreeKeyDown(self,event):
1971        'Not sure what this does'
1972        key = event.GetKeyCode()
1973        item = self.PickId
1974        if type(item) is int: return # is this the toplevel in tree?
1975        if key == wx.WXK_UP:
1976            self.oldFocus = wx.Window.FindFocus()
1977            self.PatternTree.GetPrevSibling(item)
1978        elif key == wx.WXK_DOWN:
1979            self.oldFocus = wx.Window.FindFocus()
1980            self.PatternTree.GetNextSibling(item)
1981               
1982    def OnReadPowderPeaks(self,event):
1983        'Bound to menu Data/Read Powder Peaks -- still needed?'
1984        Cuka = 1.54052
1985        self.CheckNotebook()
1986        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
1987            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
1988        try:
1989            if dlg.ShowModal() == wx.ID_OK:
1990                self.HKL = []
1991                self.powderfile = dlg.GetPath()
1992                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
1993                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
1994                data = ['PKS',Cuka,0.0]
1995                names = ['Type','Lam','Zero'] 
1996                codes = [0,0,0]
1997                inst = [G2IO.makeInstDict(names,data,codes),{}]
1998                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1999                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2000                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),peaks)
2001                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2002                self.PatternTree.Expand(Id)
2003                self.PatternTree.SelectItem(Id)
2004                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2005        finally:
2006            dlg.Destroy()
2007                       
2008    def OnImageRead(self,event):
2009        'Called to read in an image in any known format'
2010        self.CheckNotebook()
2011        dlg = wx.FileDialog(
2012            self, 'Choose image files', '.', '',
2013            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.png)|'
2014            '*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.png;*.zip|'
2015            'European detector file (*.edf)|*.edf|'
2016            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2017            'MAR file (*.mar*)|*.mar*|'
2018            'GE Image (*.avg;*.sum)|*.avg;*.sum|'
2019            'ADSC Image (*.img)|*.img|'
2020            'GSAS-II Image (*.G2img)|*.G2img|'
2021            'Portable Network Graphics image (*.png)|*.png|'
2022            'Zip archive (*.zip)|*.zip|'
2023            'All files (*.*)|*.*',
2024            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
2025        try:
2026            if dlg.ShowModal() == wx.ID_OK:
2027                imagefiles = dlg.GetPaths()
2028                imagefiles.sort()
2029                for imagefile in imagefiles:
2030                    # if a zip file, open and extract
2031                    if os.path.splitext(imagefile)[1].lower() == '.zip':
2032                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
2033                        if extractedfile is not None and extractedfile != imagefile:
2034                            imagefile = extractedfile
2035                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
2036                    if Comments:
2037                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
2038                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2039                        Imax = np.amax(Image)
2040                        Imin = max(0.0,np.amin(Image))          #force positive
2041                        if self.imageDefault:
2042                            Data = copy.copy(self.imageDefault)
2043                            Data['showLines'] = True
2044                            Data['ring'] = []
2045                            Data['rings'] = []
2046                            Data['cutoff'] = 10
2047                            Data['pixLimit'] = 20
2048                            Data['edgemin'] = 100000000
2049                            Data['calibdmin'] = 0.5
2050                            Data['calibskip'] = 0
2051                            Data['ellipses'] = []
2052                            Data['calibrant'] = ''
2053                            Data['GonioAngles'] = [0.,0.,0.]
2054                            Data['DetDepth'] = 0.
2055                            Data['DetDepthRef'] = False
2056                        else:
2057                            Data['type'] = 'PWDR'
2058                            Data['color'] = 'Paired'
2059                            Data['tilt'] = 0.0
2060                            Data['rotation'] = 0.0
2061                            Data['showLines'] = False
2062                            Data['ring'] = []
2063                            Data['rings'] = []
2064                            Data['cutoff'] = 10
2065                            Data['pixLimit'] = 20
2066                            Data['calibdmin'] = 0.5
2067                            Data['calibskip'] = 0
2068                            Data['edgemin'] = 100000000
2069                            Data['ellipses'] = []
2070                            Data['GonioAngles'] = [0.,0.,0.]
2071                            Data['DetDepth'] = 0.
2072                            Data['DetDepthRef'] = False
2073                            Data['calibrant'] = ''
2074                            Data['IOtth'] = [2.0,5.0]
2075                            Data['LRazimuth'] = [135,225]
2076                            Data['azmthOff'] = 0.0
2077                            Data['outChannels'] = 2500
2078                            Data['outAzimuths'] = 1
2079                            Data['centerAzm'] = False
2080                            Data['fullIntegrate'] = False
2081                            Data['setRings'] = False
2082                            Data['background image'] = ['',-1.0]                           
2083                            Data['dark image'] = ['',-1.0]
2084                        Data['setDefault'] = False
2085                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2086                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
2087                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2088                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2089                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2090                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'strain':np.zeros((3,3))})
2091                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
2092                        self.PickId = Id
2093                        self.Image = Id
2094                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!               
2095                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
2096        finally:
2097            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2098            os.chdir(path)
2099            dlg.Destroy()
2100
2101    def CheckNotebook(self):
2102        '''Make sure the data tree has the minimally expected controls.
2103        (BHT) correct?
2104        '''
2105        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2106            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2107            self.PatternTree.SetItemPyData(sub,[''])
2108        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2109            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2110            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2111        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2112            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2113            self.PatternTree.SetItemPyData(sub,{})
2114        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2115            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2116            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2117        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2118            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2119            self.PatternTree.SetItemPyData(sub,{})
2120        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2121            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2122            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2123                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2124               
2125    class CopyDialog(wx.Dialog):
2126        '''Creates a dialog for copying control settings between
2127        data tree items'''
2128        def __init__(self,parent,title,text,data):
2129            wx.Dialog.__init__(self,parent,-1,title, 
2130                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2131            self.data = data
2132            panel = wx.Panel(self)
2133            mainSizer = wx.BoxSizer(wx.VERTICAL)
2134            topLabl = wx.StaticText(panel,-1,text)
2135            mainSizer.Add((10,10),1)
2136            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2137            mainSizer.Add((10,10),1)
2138            ncols = len(data)/40+1
2139            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=ncols,hgap=2,vgap=2)
2140            for id,item in enumerate(self.data):
2141                ckbox = wx.CheckBox(panel,id,item[1])
2142                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2143                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2144            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2145            OkBtn = wx.Button(panel,-1,"Ok")
2146            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2147            cancelBtn = wx.Button(panel,-1,"Cancel")
2148            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2149            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2150            btnSizer.Add((20,20),1)
2151            btnSizer.Add(OkBtn)
2152            btnSizer.Add((20,20),1)
2153            btnSizer.Add(cancelBtn)
2154            btnSizer.Add((20,20),1)
2155           
2156            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2157            panel.SetSizer(mainSizer)
2158            panel.Fit()
2159            self.Fit()
2160       
2161        def OnCopyChange(self,event):
2162            id = event.GetId()
2163            self.data[id][0] = self.FindWindowById(id).GetValue()       
2164           
2165        def OnOk(self,event):
2166            parent = self.GetParent()
2167            parent.Raise()
2168            self.EndModal(wx.ID_OK)             
2169           
2170        def OnCancel(self,event):
2171            parent = self.GetParent()
2172            parent.Raise()
2173            self.EndModal(wx.ID_CANCEL)             
2174           
2175        def GetData(self):
2176            return self.data
2177       
2178    class SumDialog(wx.Dialog):
2179        'Allows user to supply scale factor(s) when summing data'
2180        def __init__(self,parent,title,text,dataType,data):
2181            wx.Dialog.__init__(self,parent,-1,title, 
2182                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2183            self.data = data
2184            panel = wx.Panel(self)
2185            mainSizer = wx.BoxSizer(wx.VERTICAL)
2186            topLabl = wx.StaticText(panel,-1,text)
2187            mainSizer.Add((10,10),1)
2188            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2189            mainSizer.Add((10,10),1)
2190            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
2191            for id,item in enumerate(self.data[:-1]):
2192                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2193                name.SetEditable(False)
2194                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2195                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2196                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2197                dataGridSizer.Add(scale,0,wx.LEFT,10)
2198                dataGridSizer.Add(name,0,wx.RIGHT,10)
2199            if dataType:
2200                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2201                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2202                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2203                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2204                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2205                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2206            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2207            OkBtn = wx.Button(panel,-1,"Ok")
2208            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2209            cancelBtn = wx.Button(panel,-1,"Cancel")
2210            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2211            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2212            btnSizer.Add((20,20),1)
2213            btnSizer.Add(OkBtn)
2214            btnSizer.Add((20,20),1)
2215            btnSizer.Add(cancelBtn)
2216            btnSizer.Add((20,20),1)
2217           
2218            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2219            panel.SetSizer(mainSizer)
2220            panel.Fit()
2221            self.Fit()
2222
2223        def OnScaleChange(self,event):
2224            id = event.GetId()
2225            value = self.FindWindowById(id).GetValue()
2226            try:
2227                self.data[id][0] = float(value)
2228                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2229            except ValueError:
2230                if value and '-' not in value[0]:
2231                    print 'bad input - numbers only'
2232                    self.FindWindowById(id).SetValue('0.000')
2233           
2234        def OnNameChange(self,event):
2235            self.data[-1] = self.name.GetValue() 
2236           
2237        def OnOk(self,event):
2238            parent = self.GetParent()
2239            parent.Raise()
2240            self.EndModal(wx.ID_OK)             
2241           
2242        def OnCancel(self,event):
2243            parent = self.GetParent()
2244            parent.Raise()
2245            self.EndModal(wx.ID_CANCEL)             
2246           
2247        def GetData(self):
2248            return self.data
2249                       
2250    def OnPwdrSum(self,event):
2251        'Sum together powder data(?)'
2252        TextList = []
2253        DataList = []
2254        SumList = []
2255        Names = []
2256        Inst = None
2257        SumItemList = []
2258        Comments = ['Sum equals: \n']
2259        if self.PatternTree.GetCount():
2260            item, cookie = self.PatternTree.GetFirstChild(self.root)
2261            while item:
2262                name = self.PatternTree.GetItemText(item)
2263                Names.append(name)
2264                if 'PWDR' in name:
2265                    TextList.append([0.0,name])
2266                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2267                    if not Inst:
2268                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2269                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2270            if len(TextList) < 2:
2271                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2272                return
2273            TextList.append('default_sum_name')               
2274            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2275            try:
2276                if dlg.ShowModal() == wx.ID_OK:
2277                    lenX = 0
2278                    Xminmax = [0,0]
2279                    Xsum = []
2280                    Ysum = []
2281                    Vsum = []
2282                    result = dlg.GetData()
2283                    for i,item in enumerate(result[:-1]):
2284                        scale,name = item
2285                        data = DataList[i]
2286                        if scale:
2287                            Comments.append("%10.3f %s" % (scale,' * '+name))
2288                            x,y,w,yc,yb,yd = data   #numpy arrays!
2289                            v = 1./w
2290                            if lenX:
2291                                if lenX != len(x):
2292                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2293                                        '\nExpected:'+str(lenX)+ \
2294                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2295                                    return
2296                            else:
2297                                lenX = len(x)
2298                            if Xminmax[1]:
2299                                if Xminmax != [x[0],x[-1]]:
2300                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2301                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2302                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2303                                    return
2304                                else:
2305                                    for j,yi in enumerate(y):
2306                                         Ysum[j] += scale*yi
2307                                         Vsum[j] += abs(scale)*v[j]
2308                            else:
2309                                Xminmax = [x[0],x[-1]]
2310                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2311                                for j,yi in enumerate(y):
2312                                    Xsum.append(x[j])
2313                                    Ysum.append(scale*yi)
2314                                    Vsum.append(abs(scale*v[j]))
2315                    Wsum = 1./np.array(Vsum)
2316                    outname = 'PWDR '+result[-1]
2317                    Id = 0
2318                    if outname in Names:
2319                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2320                        try:
2321                            if dlg2.ShowModal() == wx.ID_OK:
2322                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2323                                self.PatternTree.Delete(Id)
2324                        finally:
2325                            dlg2.Destroy()
2326                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2327                    if Id:
2328                        Sample = G2pdG.SetDefaultSample()
2329                        valuesdict = {
2330                            'wtFactor':1.0,
2331                            'Dummy':False,
2332                            'ranId':ran.randint(0,sys.maxint),
2333                            }
2334                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2335                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2336                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2337                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2338                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2339                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2340                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2341                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2342                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
2343                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
2344                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2345                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2346                        self.PatternTree.SelectItem(Id)
2347                        self.PatternTree.Expand(Id)
2348            finally:
2349                dlg.Destroy()
2350
2351    def OnImageSum(self,event):
2352        'Sum together image data(?)'
2353        TextList = []
2354        DataList = []
2355        SumList = []
2356        Names = []
2357        Inst = []
2358        SumItemList = []
2359        Comments = ['Sum equals: \n']
2360        if self.PatternTree.GetCount():
2361            item, cookie = self.PatternTree.GetFirstChild(self.root)
2362            while item:
2363                name = self.PatternTree.GetItemText(item)
2364                Names.append(name)
2365                if 'IMG' in name:
2366                    TextList.append([0.0,name])
2367                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2368                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2369                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2370            if len(TextList) < 2:
2371                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2372                return
2373            TextList.append('default_sum_name')               
2374            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2375            try:
2376                if dlg.ShowModal() == wx.ID_OK:
2377                    imSize = 0
2378                    result = dlg.GetData()
2379                    First = True
2380                    Found = False
2381                    for i,item in enumerate(result[:-1]):
2382                        scale,name = item
2383                        data = DataList[i]
2384                        if scale:
2385                            Found = True                               
2386                            Comments.append("%10.3f %s" % (scale,' * '+name))
2387                            Npix,imagefile = data
2388                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2389                            if First:
2390                                newImage = np.zeros_like(image)
2391                                First = False
2392                            if imSize:
2393                                if imSize != Npix:
2394                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2395                                        '\nExpected:'+str(imSize)+ \
2396                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2397                                    return
2398                                newImage = newImage+scale*image
2399                            else:
2400                                imSize = Npix
2401                                newImage = newImage+scale*image
2402                            del(image)
2403                    if not Found:
2404                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2405                        return
2406                       
2407                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2408                    outname = 'IMG '+result[-1]
2409                    Id = 0
2410                    if outname in Names:
2411                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2412                        try:
2413                            if dlg2.ShowModal() == wx.ID_OK:
2414                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2415                        finally:
2416                            dlg2.Destroy()
2417                    else:
2418                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2419                    if Id:
2420                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2421                            'G2img files (*.G2img)|*.G2img', 
2422                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2423                        if dlg.ShowModal() == wx.ID_OK:
2424                            newimagefile = dlg.GetPath()
2425                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2426                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2427                            Imax = np.amax(newImage)
2428                            Imin = np.amin(newImage)
2429                            newImage = []
2430                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2431                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2432                        del(newImage)
2433                        if self.imageDefault:
2434                            Data = copy.copy(self.imageDefault)
2435                        Data['showLines'] = True
2436                        Data['ring'] = []
2437                        Data['rings'] = []
2438                        Data['cutoff'] = 10
2439                        Data['pixLimit'] = 20
2440                        Data['ellipses'] = []
2441                        Data['calibrant'] = ''
2442                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2443                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2444                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2445                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2446                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),{})
2447                        self.PatternTree.SelectItem(Id)
2448                        self.PatternTree.Expand(Id)
2449                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2450                        self.Image = self.PickId
2451            finally:
2452                dlg.Destroy()
2453                     
2454    def OnAddPhase(self,event):
2455        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2456        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2457            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2458        else:
2459            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2460        PhaseName = ''
2461        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2462            style=wx.OK)
2463        if dlg.ShowModal() == wx.ID_OK:
2464            PhaseName = dlg.GetValue()
2465        dlg.Destroy()
2466        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2467        E,SGData = G2spc.SpcGroup('P 1')
2468        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2469       
2470    def OnDeletePhase(self,event):
2471        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2472        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2473        if self.dataFrame:
2474            self.dataFrame.Clear() 
2475        TextList = []
2476        DelList = []
2477        DelItemList = []
2478        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2479            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2480        else:
2481            return
2482        if sub:
2483            item, cookie = self.PatternTree.GetFirstChild(sub)
2484            while item:
2485                TextList.append(self.PatternTree.GetItemText(item))
2486                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2487            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2488            try:
2489                if dlg.ShowModal() == wx.ID_OK:
2490                    result = dlg.GetSelections()
2491                    for i in result: DelList.append([i,TextList[i]])
2492                    item, cookie = self.PatternTree.GetFirstChild(sub)
2493                    i = 0
2494                    while item:
2495                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2496                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2497                        i += 1
2498                    for item in DelItemList:
2499                        name = self.PatternTree.GetItemText(item)
2500                        self.PatternTree.Delete(item)
2501                        self.G2plotNB.Delete(name)
2502                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2503                    while item:
2504                        name = self.PatternTree.GetItemText(item)
2505                        if 'PWDR' in name:
2506                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2507                            refList = self.PatternTree.GetItemPyData(Id)
2508                            for i,item in DelList:
2509                                del(refList[item])
2510                            self.PatternTree.SetItemPyData(Id,refList)
2511                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2512            finally:
2513                dlg.Destroy()
2514               
2515    def OnRenameData(self,event):
2516        'Renames an existing phase. Called by Data/Rename Phase menu'
2517        name = self.PatternTree.GetItemText(self.PickId)     
2518        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2519            dataType = name[:name.index(' ')+1]                 #includes the ' '
2520            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2521                defaultValue=name[name.index(' ')+1:])
2522            try:
2523                if dlg.ShowModal() == wx.ID_OK:
2524                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2525            finally:
2526                dlg.Destroy()
2527       
2528    def GetFileList(self,fileType,skip=None):        #potentially useful?
2529        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2530        fileList = []
2531        Source = ''
2532        id, cookie = self.PatternTree.GetFirstChild(self.root)
2533        while id:
2534            name = self.PatternTree.GetItemText(id)
2535            if fileType in name:
2536                if id == skip:
2537                    Source = name
2538                else:
2539                    fileList.append([False,name,id])
2540            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2541        if skip:
2542            return fileList,Source
2543        else:
2544            return fileList
2545           
2546    def OnDataDelete(self, event):
2547        '''Delete one or more histograms from data tree. Called by the
2548        Data/DeleteData menu
2549        '''
2550        TextList = ['All Data']
2551        DelList = []
2552        DelItemList = []
2553        ifPWDR = False
2554        ifSASD = False
2555        ifIMG = False
2556        ifHKLF = False
2557        ifPDF = False
2558        if self.PatternTree.GetCount():
2559            item, cookie = self.PatternTree.GetFirstChild(self.root)
2560            while item:
2561                name = self.PatternTree.GetItemText(item)
2562                if name not in ['Notebook','Controls','Covariance','Constraints',
2563                    'Restraints','Phases','Rigid bodies']:
2564                    if 'PWDR' in name: ifPWDR = True
2565                    if 'SASD' in name: ifSASD = True
2566                    if 'IMG' in name: ifIMG = True
2567                    if 'HKLF' in name: ifHKLF = True
2568                    if 'PDF' in name: ifPDF = True
2569                    TextList.append(name)
2570                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2571            if ifPWDR: TextList.insert(1,'All PWDR')
2572            if ifSASD: TextList.insert(1,'All SASD')
2573            if ifIMG: TextList.insert(1,'All IMG')
2574            if ifHKLF: TextList.insert(1,'All HKLF')
2575            if ifPDF: TextList.insert(1,'All PDF')               
2576            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2577            try:
2578                if dlg.ShowModal() == wx.ID_OK:
2579                    result = dlg.GetSelections()
2580                    for i in result: DelList.append(TextList[i])
2581                    if 'All Data' in DelList:
2582                        DelList = [item for item in TextList if item[:3] != 'All']
2583                    elif 'All PWDR' in DelList:
2584                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2585                    elif 'All SASD' in DelList:
2586                        DelList = [item for item in TextList if item[:4] == 'SASD']
2587                    elif 'All IMG' in DelList:
2588                        DelList = [item for item in TextList if item[:3] == 'IMG']
2589                    elif 'All HKLF' in DelList:
2590                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2591                    elif 'All PDF' in DelList:
2592                        DelList = [item for item in TextList if item[:3] == 'PDF']
2593                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2594                    while item:
2595                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2596                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2597                    for item in DelItemList:
2598                        self.PatternTree.Delete(item)
2599                    self.PickId = 0
2600                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2601            finally:
2602                dlg.Destroy()
2603
2604    def OnFileOpen(self, event, filename=None):
2605        '''Reads in a GSAS-II .gpx project file in response to the
2606        File/Open Project menu button
2607        '''
2608        result = wx.ID_OK
2609        Id = 0
2610        self.EnablePlot = False
2611        if self.PatternTree.GetChildrenCount(self.root,False):
2612            if self.dataFrame:
2613                self.dataFrame.Clear() 
2614            dlg = wx.MessageDialog(
2615                self,
2616                'Do you want to overwrite the current project? '
2617                'Any unsaved changes will be lost. Press OK to continue.',
2618                'Overwrite?',  wx.OK | wx.CANCEL)
2619            try:
2620                result = dlg.ShowModal()
2621                if result == wx.ID_OK:
2622                    self.PatternTree.DeleteChildren(self.root)
2623                    self.GSASprojectfile = ''
2624                    if self.HKL: self.HKL = []
2625                    if self.G2plotNB.plotList:
2626                        self.G2plotNB.clear()
2627            finally:
2628                dlg.Destroy()
2629        if result != wx.ID_OK: return
2630
2631        if not filename:
2632            if self.dataDisplay: self.dataDisplay.Destroy()
2633            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2634                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2635            try:
2636                if dlg.ShowModal() != wx.ID_OK: return
2637                self.GSASprojectfile = dlg.GetPath()
2638                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2639                self.dirname = dlg.GetDirectory()
2640            finally:
2641                dlg.Destroy()
2642        else:
2643            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2644            self.dirname = os.path.split(filename)[0]
2645
2646        G2IO.ProjFileOpen(self)
2647        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2648        self.PatternTree.Expand(self.root)
2649        self.HKL = []
2650        item, cookie = self.PatternTree.GetFirstChild(self.root)
2651        while item and not Id:
2652            name = self.PatternTree.GetItemText(item)
2653            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
2654                Id = item
2655            elif name == 'Controls':
2656                data = self.PatternTree.GetItemPyData(item)
2657                if data:
2658                    for item in self.Refine: item.Enable(True)
2659                    self.EnableSeqRefineMenu()
2660            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2661        if Id:
2662            self.EnablePlot = True
2663            self.PatternTree.SelectItem(Id)
2664        self.CheckNotebook()
2665        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2666
2667    def OnFileClose(self, event):
2668        '''Clears the data tree in response to the
2669        File/New Project menu button. User is given option to save
2670        the project.
2671        '''
2672        if self.dataFrame:
2673            self.dataFrame.Clear()
2674            self.dataFrame.SetLabel('GSAS-II data display') 
2675        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2676        try:
2677            result = dlg.ShowModal()
2678            if result == wx.ID_OK:
2679                self.OnFileSaveMenu(event)
2680            if result != wx.ID_CANCEL:
2681                self.GSASprojectfile = ''
2682                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2683                self.PatternTree.DeleteChildren(self.root)
2684                if self.HKL: self.HKL = []
2685                if self.G2plotNB.plotList:
2686                    self.G2plotNB.clear()
2687        finally:
2688            dlg.Destroy()
2689
2690    def OnFileSave(self, event):
2691        '''Save the current project in response to the
2692        File/Save Project menu button
2693        '''
2694       
2695        if self.GSASprojectfile: 
2696            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2697            G2IO.ProjFileSave(self)
2698        else:
2699            self.OnFileSaveas(event)
2700
2701    def OnFileSaveas(self, event):
2702        '''Save the current project in response to the
2703        File/Save as menu button
2704        '''
2705        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2706            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2707        try:
2708            if dlg.ShowModal() == wx.ID_OK:
2709                self.GSASprojectfile = dlg.GetPath()
2710                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2711                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2712                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2713                G2IO.ProjFileSave(self)
2714                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2715        finally:
2716            dlg.Destroy()
2717
2718    def ExitMain(self, event):
2719        '''Called if the main window is closed'''
2720        if self.undofile:
2721            os.remove(self.undofile)
2722        sys.exit()
2723       
2724    def OnFileExit(self, event):
2725        '''Called in response to the File/Quit menu button'''
2726        if self.dataFrame:
2727            self.dataFrame.Clear() 
2728            self.dataFrame.Destroy()
2729        self.Close()
2730       
2731    # def OnExportPatterns(self,event):
2732    #     names = ['All']
2733    #     exports = []
2734    #     item, cookie = self.PatternTree.GetFirstChild(self.root)
2735    #     while item:
2736    #         name = self.PatternTree.GetItemText(item)
2737    #         if 'PWDR' in name:
2738    #             names.append(name)
2739    #         item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2740    #     if names:
2741    #         dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2742    #         if dlg.ShowModal() == wx.ID_OK:
2743    #             sel = dlg.GetSelections()
2744    #             if sel[0] == 0:
2745    #                 exports = names[1:]
2746    #             else:
2747    #                 for x in sel:
2748    #                     exports.append(names[x])
2749    #         dlg.Destroy()
2750    #     if exports:
2751    #         dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '',
2752    #             'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2753    #             wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2754    #         try:
2755    #             if dlg.ShowModal() == wx.ID_OK:
2756    #                 powderfile = dlg.GetPath()
2757    #                 powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2758    #                 if 'fxye' in powderfile:
2759    #                     G2IO.powderFxyeSave(self,exports,powderfile)
2760    #                 else:       #just xye
2761    #                     G2IO.powderXyeSave(self,exports,powderfile)
2762    #         finally:
2763    #             dlg.Destroy()
2764       
2765    def OnExportPeakList(self,event):
2766        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2767            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2768        try:
2769            if dlg.ShowModal() == wx.ID_OK:
2770                self.peaklistfile = dlg.GetPath()
2771                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2772                file = open(self.peaklistfile,'w')               
2773                item, cookie = self.PatternTree.GetFirstChild(self.root)
2774                while item:
2775                    name = self.PatternTree.GetItemText(item)
2776                    if 'PWDR' in name:
2777                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2778                        while item2:
2779                            name2 = self.PatternTree.GetItemText(item2)
2780                            if name2 == 'Peak List':
2781                                peaks = self.PatternTree.GetItemPyData(item2)
2782                                file.write("%s \n" % (name+' Peak List'))               
2783                                for peak in peaks:
2784                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2785                                        (peak[0],peak[2],peak[4],peak[6]))
2786                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2787                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2788                file.close()
2789        finally:
2790            dlg.Destroy()
2791       
2792    def OnExportHKL(self,event):
2793        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2794            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2795        try:
2796            if dlg.ShowModal() == wx.ID_OK:
2797                self.peaklistfile = dlg.GetPath()
2798                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2799                file = open(self.peaklistfile,'w')               
2800                item, cookie = self.PatternTree.GetFirstChild(self.root)
2801                while item:
2802                    name = self.PatternTree.GetItemText(item)
2803                    if 'PWDR' in name:
2804                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2805                        while item2:
2806                            name2 = self.PatternTree.GetItemText(item2)
2807                            if name2 == 'Reflection Lists':
2808                                data = self.PatternTree.GetItemPyData(item2)
2809                                phases = data.keys()
2810                                for phase in phases:
2811                                    peaks = data[phase]
2812                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2813                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2814                                    for peak in peaks:
2815                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2816                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2817                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2818                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2819                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2820                file.close()
2821        finally:
2822            dlg.Destroy()
2823       
2824    def OnExportPDF(self,event):
2825        #need S(Q) and G(R) to be saved here - probably best from selection?
2826        names = ['All']
2827        exports = []
2828        item, cookie = self.PatternTree.GetFirstChild(self.root)
2829        while item:
2830            name = self.PatternTree.GetItemText(item)
2831            if 'PDF' in name:
2832                names.append(name)
2833            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2834        if names:
2835            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2836            if dlg.ShowModal() == wx.ID_OK:
2837                sel = dlg.GetSelections()
2838                if sel[0] == 0:
2839                    exports = names[1:]
2840                else:
2841                    for x in sel:
2842                        exports.append(names[x])
2843            dlg.Destroy()
2844        if exports:
2845            G2IO.PDFSave(self,exports)
2846       
2847    def OnMakePDFs(self,event):
2848        '''Calculates PDFs
2849        '''
2850        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2851        TextList = ['All PWDR']
2852        PDFlist = []
2853        Names = []
2854        if self.PatternTree.GetCount():
2855            id, cookie = self.PatternTree.GetFirstChild(self.root)
2856            while id:
2857                name = self.PatternTree.GetItemText(id)
2858                Names.append(name)
2859                if 'PWDR' in name:
2860                    TextList.append(name)
2861                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2862            if len(TextList) == 1:
2863                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2864                return
2865            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2866            try:
2867                if dlg.ShowModal() == wx.ID_OK:
2868                    result = dlg.GetSelections()
2869                    for i in result: PDFlist.append(TextList[i])
2870                    if 0 in result:
2871                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2872                    for item in PDFlist:
2873                        PWDRname = item[4:]
2874                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2875                        Data = {
2876                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2877                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2878                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2879                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2880                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2881                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2882                            'Lorch':True,}
2883                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2884                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2885                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2886                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2887                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2888                for item in self.ExportPDF: item.Enable(True)
2889            finally:
2890                dlg.Destroy()
2891               
2892    def GetPWDRdatafromTree(self,PWDRname):
2893        ''' Returns powder data from GSASII tree
2894
2895        :param str PWDRname: a powder histogram name as obtained from
2896          :meth:`GSASIIstruct.GetHistogramNames`
2897
2898        :returns: PWDRdata = powder data dictionary with
2899          Powder data arrays, Limits, Instrument Parameters,
2900          Sample Parameters           
2901        '''
2902        PWDRdata = {}
2903        try:
2904            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2905        except ValueError:
2906            PWDRdata['wtFactor'] = 1.0
2907        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2908        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2909        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2910        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2911        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2912        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2913        if 'ranId' not in PWDRdata:  # patch, add a random Id
2914            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
2915        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
2916            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
2917        return PWDRdata
2918
2919    def GetHKLFdatafromTree(self,HKLFname):
2920        ''' Returns single crystal data from GSASII tree
2921
2922        :param str HKLFname: a single crystal histogram name as obtained
2923          from
2924          :meth:`GSASIIstruct.GetHistogramNames`
2925
2926        :returns: HKLFdata = single crystal data list of reflections
2927
2928        '''
2929        HKLFdata = {}
2930        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2931#        try:
2932#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2933#        except ValueError:
2934#            HKLFdata['wtFactor'] = 1.0
2935        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2936        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2937        return HKLFdata
2938       
2939    def GetPhaseData(self):
2940        '''Returns a dict with defined phases.
2941        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
2942        get same info from GPX file.
2943        '''
2944        phaseData = {}
2945        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2946            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2947        else:
2948            print 'no phases found in GetPhaseData'
2949            sub = None
2950        if sub:
2951            item, cookie = self.PatternTree.GetFirstChild(sub)
2952            while item:
2953                phaseName = self.PatternTree.GetItemText(item)
2954                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2955                if 'ranId' not in phaseData[phaseName]:
2956                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2957                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2958        return phaseData
2959
2960    def GetPhaseInfofromTree(self):
2961        '''Get the phase names and their rId values,
2962        also the histograms used in each phase.
2963
2964        :returns: (phaseRIdList, usedHistograms) where
2965
2966          * phaseRIdList is a list of random Id values for each phase
2967          * usedHistograms is a dict where the keys are the phase names
2968            and the values for each key are a list of the histogram names
2969            used in each phase.
2970        '''
2971        phaseRIdList = []
2972        usedHistograms = {}
2973        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2974        if sub:
2975            item, cookie = self.PatternTree.GetFirstChild(sub)
2976            while item:
2977                phaseName = self.PatternTree.GetItemText(item)
2978                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2979                if ranId: phaseRIdList.append(ranId)
2980                data = self.PatternTree.GetItemPyData(item)
2981                UseList = data['Histograms']
2982                usedHistograms[phaseName] = UseList.keys()
2983                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2984        return phaseRIdList,usedHistograms
2985
2986    def GetPhaseNames(self):
2987        '''Returns a list of defined phases.
2988        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
2989        get same info from GPX file.
2990        '''
2991        phaseNames = []
2992        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2993            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2994        else:
2995            print 'no phases found in GetPhaseNames'
2996            sub = None
2997        if sub:
2998            item, cookie = self.PatternTree.GetFirstChild(sub)
2999            while item:
3000                phase = self.PatternTree.GetItemText(item)
3001                phaseNames.append(phase)
3002                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3003        return phaseNames
3004   
3005    def GetHistogramNames(self,hType):
3006        """ Returns a list of histogram names found in the GSASII data tree
3007        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3008        get same info from GPX file.
3009       
3010        :param str hType: list of histogram types
3011        :return: list of histogram names
3012       
3013        """
3014        HistogramNames = []
3015        if self.PatternTree.GetCount():
3016            item, cookie = self.PatternTree.GetFirstChild(self.root)
3017            while item:
3018                name = self.PatternTree.GetItemText(item)
3019                if name[:4] in hType:
3020                    HistogramNames.append(name)       
3021                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3022
3023        return HistogramNames
3024
3025                   
3026    def GetUsedHistogramsAndPhasesfromTree(self):
3027        ''' Returns all histograms that are found in any phase
3028        and any phase that uses a histogram.
3029        This also assigns numbers to used phases and histograms by the
3030        order they appear in the file.
3031        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3032        get same info from GPX file.
3033
3034        :returns: (Histograms,Phases)
3035
3036            * Histograms = dictionary of histograms as {name:data,...}
3037            * Phases = dictionary of phases that use histograms
3038        '''
3039        Histograms = {}
3040        Phases = {}
3041        phaseNames = self.GetPhaseNames()
3042        phaseData = self.GetPhaseData()
3043        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3044
3045        for phase in phaseData:
3046            Phase = phaseData[phase]
3047            pId = phaseNames.index(phase)
3048            Phase['pId'] = pId
3049            if Phase['Histograms']:
3050                if phase not in Phases:
3051                    Phases[phase] = Phase
3052                for hist in Phase['Histograms']:
3053                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3054                        Phase['Histograms'][hist]['Use'] = True         
3055                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3056                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3057                        if item:
3058                            if 'PWDR' in hist[:4]: 
3059                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3060                            elif 'HKLF' in hist[:4]:
3061                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3062                            hId = histoList.index(hist)
3063                            Histograms[hist]['hId'] = hId
3064                        else: # would happen if a referenced histogram were renamed or deleted
3065                            print('For phase "'+str(phase)+
3066                                  '" unresolved reference to histogram "'+str(hist)+'"')
3067        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3068        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3069        return Histograms,Phases
3070       
3071    def MakeLSParmDict(self):
3072        '''Load all parameters used for computation from the tree into a
3073        dict of paired values [value, refine flag]. Note that this is
3074        different than the parmDict used in the refinement, which only has
3075        values.
3076
3077        Note that similar things are done in
3078        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3079        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3080        a GPX file).
3081
3082        :returns: (parmDict,varyList) where:
3083
3084         * parmDict is a dict with values and refinement flags
3085           for each parameter and
3086         * varyList is a list of variables (refined parameters).
3087        '''
3088        parmDict = {}
3089        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3090        for phase in Phases:
3091            if 'pId' not in Phases[phase]:
3092                self.ErrorDialog('View parameter error','You must run least squares at least once')
3093                raise Exception,'No pId for phase '+str(phase)
3094        rigidbodyDict = self.PatternTree.GetItemPyData(   
3095            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3096        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3097        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3098        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3099        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3100        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3101        varyList = rbVary+phaseVary+hapVary+histVary
3102        parmDict.update(rbDict)
3103        parmDict.update(phaseDict)
3104        parmDict.update(hapDict)
3105        parmDict.update(histDict)
3106        for parm in parmDict:
3107            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3108                'Omega','Chi','Phi','nDebye','nPeaks']:
3109                parmDict[parm] = [parmDict[parm],'-']
3110            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3111                parmDict[parm] = [parmDict[parm],'-']
3112            elif parm in varyList:
3113                parmDict[parm] = [parmDict[parm],'T']
3114            else:
3115                parmDict[parm] = [parmDict[parm],'F']
3116        # for i in parmDict: print i,'\t',parmDict[i]
3117        # fl = open('parmDict.dat','wb')
3118        # import cPickle
3119        # cPickle.dump(parmDict,fl,1)
3120        # fl.close()
3121        return parmDict,varyList
3122
3123    def ShowLSParms(self,event):
3124        '''Displays a window showing all parameters in the refinement.
3125        Called from the Calculate/View LS Parms menu.
3126        '''
3127        parmDict,varyList = self.MakeLSParmDict()
3128        parmValDict = {}
3129        for i in parmDict:
3130            parmValDict[i] = parmDict[i][0]
3131           
3132        reqVaryList = tuple(varyList) # save requested variables
3133        try:
3134            # process constraints
3135            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3136            Constraints = self.PatternTree.GetItemPyData(sub)
3137            constList = []
3138            for item in Constraints:
3139                if item.startswith('_'): continue
3140                constList += Constraints[item]
3141            G2mv.InitVars()
3142            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3143            groups,parmlist = G2mv.GroupConstraints(constrDict)
3144            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3145            G2mv.Map2Dict(parmValDict,varyList)
3146        except:
3147            pass
3148        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3149        dlg.ShowModal()
3150        dlg.Destroy()
3151       
3152    def OnRefine(self,event):
3153        '''Perform a refinement.
3154        Called from the Calculate/Refine menu.
3155        '''       
3156        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3157        if Id:
3158            dlg = wx.MessageDialog(
3159                self,
3160                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3161                'Remove sequential results?',wx.OK|wx.CANCEL)
3162            if dlg.ShowModal() == wx.ID_OK:
3163                self.PatternTree.Delete(Id)
3164                dlg.Destroy()
3165            else:
3166                dlg.Destroy()
3167                return
3168
3169        self.OnFileSave(event)
3170        # check that constraints are OK here
3171        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3172        if errmsg:
3173            print('Error in constraints:\n'+errmsg+
3174                  '\nRefinement not possible')
3175            self.ErrorDialog('Constraint Error',
3176                             'Error in constraints:\n'+errmsg+
3177                             '\nRefinement not possible')
3178            return
3179        if warnmsg:
3180            print('Conflict between refinment flag settings and constraints:\n'+
3181                  warnmsg+'\nRefinement not possible')
3182            self.ErrorDialog('Refinement Flag Error',
3183                             'Conflict between refinment flag settings and constraints:\n'+
3184                             warnmsg+
3185                             '\nRefinement not possible')
3186            return
3187        #works - but it'd be better if it could restore plots
3188        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3189            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3190        screenSize = wx.ClientDisplayRect()
3191        Size = dlg.GetSize()
3192        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3193        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3194        dlg.SetSize(Size)
3195        Rw = 100.00
3196        try:
3197            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3198        finally:
3199            dlg.Destroy()
3200        oldId =  self.PatternTree.GetSelection()        #retain current selection
3201        oldName = self.PatternTree.GetItemText(oldId)
3202        parentId = self.PatternTree.GetItemParent(oldId)
3203        parentName = ''
3204        if parentId:
3205            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3206        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3207        try:
3208            if dlg.ShowModal() == wx.ID_OK:
3209                Id = 0
3210                self.PatternTree.DeleteChildren(self.root)
3211                if self.HKL: self.HKL = []
3212                if self.G2plotNB.plotList:
3213                    self.G2plotNB.clear()
3214                G2IO.ProjFileOpen(self)
3215                item, cookie = self.PatternTree.GetFirstChild(self.root)
3216                while item and not Id:
3217                    name = self.PatternTree.GetItemText(item)
3218                    if name[:4] in ['PWDR','HKLF']:
3219                        Id = item
3220                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3221                if Id:
3222                    self.PatternTree.SelectItem(Id)
3223                if parentName:
3224                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3225                    if parentId:
3226                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3227                    else:
3228                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3229                    self.PatternTree.SelectItem(itemId)
3230        finally:
3231            dlg.Destroy()
3232
3233    def OnSeqRefine(self,event):
3234        '''Perform a sequential refinement.
3235        Called from the Calculate/Sequential refine menu.
3236        '''       
3237        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3238        if not Id:
3239            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3240            self.PatternTree.SetItemPyData(Id,{})           
3241        self.OnFileSave(event)
3242        # check that constraints are OK here
3243        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3244        if errmsg:
3245            print('Error in constraints:\n'+errmsg+
3246                  '\nRefinement not possible')
3247            self.ErrorDialog('Constraint Error',
3248                             'Error in constraints:\n'+errmsg+
3249                             '\nRefinement not possible')
3250            return
3251        if warnmsg:
3252            print('Conflict between refinment flag settings and constraints:\n'+
3253                  warnmsg+'\nRefinement not possible')
3254            self.ErrorDialog('Refinement Flag Error',
3255                             'Conflict between refinment flag settings and constraints:\n'+
3256                             warnmsg+'\nRefinement not possible')
3257            return
3258        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3259            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3260        screenSize = wx.ClientDisplayRect()
3261        Size = dlg.GetSize()
3262        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3263        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3264        dlg.SetSize(Size)
3265        try:
3266            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3267        finally:
3268            dlg.Destroy()       
3269        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3270        try:
3271            if dlg.ShowModal() == wx.ID_OK:
3272                Id = 0
3273                self.PatternTree.DeleteChildren(self.root)
3274                if self.HKL: self.HKL = []
3275                if self.G2plotNB.plotList:
3276                    self.G2plotNB.clear()
3277                G2IO.ProjFileOpen(self)
3278                Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3279                self.PatternTree.SelectItem(Id)
3280
3281        finally:
3282            dlg.Destroy()
3283       
3284    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3285        'Display an error message'
3286        result = None
3287        if parent is None:
3288            dlg = wx.MessageDialog(self, message, title,  wtype)
3289        else:
3290            dlg = wx.MessageDialog(parent, message, title,  wtype)
3291            dlg.CenterOnParent() # not working on Mac
3292        try:
3293            result = dlg.ShowModal()
3294        finally:
3295            dlg.Destroy()
3296        return result
3297
3298class GSASIImain(wx.App):
3299    '''Defines a wxApp for GSAS-II
3300
3301    Creates a wx frame (self.main) which contains the display of the
3302    data tree.
3303    '''
3304    def OnInit(self):
3305        '''Called automatically when the app is created.'''
3306        if '2.7' not in sys.version[:5]:
3307            dlg = wx.MessageDialog(None, 
3308                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version[:5],
3309                'Python version error',  wx.OK)
3310            try:
3311                result = dlg.ShowModal()
3312            finally:
3313                dlg.Destroy()
3314            sys.exit()
3315        self.main = GSASII(None)
3316        self.main.Show()
3317        self.SetTopWindow(self.main)
3318        # DEBUG: jump to sequential results
3319        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3320        #self.main.PatternTree.SelectItem(Id)
3321        # end DEBUG
3322        return True
3323    def MacOpenFile(self, filename):
3324        '''Called on Mac every time a file is dropped on the app when it is running,
3325        treat this like a File/Open project menu action.
3326        Should be ignored on other platforms
3327        '''
3328        # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3329        if os.path.splitext(filename)[1] == '.py': return
3330        # end PATCH
3331        self.main.OnFileOpen(None,filename)
3332
3333def main():
3334    '''Start up the GSAS-II application'''
3335    #application = GSASIImain() # don't redirect output, someday we
3336    # may want to do this if we can
3337    application = GSASIImain(0)
3338    if wxInspector:
3339        import wx.lib.inspection as wxeye
3340        wxeye.InspectionTool().Show()
3341
3342    #application.main.OnRefine(None)
3343    application.MainLoop()
3344   
3345if __name__ == '__main__':
3346    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.