source: trunk/GSASII.py @ 1378

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

wxPython 2.9 fixes: FlexGridSizer?, wx.Colour, MacOpenFile?; move bind to prevent Rigid body crash

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