source: trunk/GSASII.py @ 1304

Last change on this file since 1304 was 1304, checked in by vondreele, 9 years ago

forgot to clear the flag

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