source: trunk/GSASII.py @ 1303

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

selection of Tutorials from the Help menu forces a SetDirectory? in FileDialog? to be GSASII/Exercises

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