source: trunk/GSASII.py @ 1282

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

sequential refinement updates

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