source: trunk/GSASII.py @ 1261

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

reorg exports to implement directory selection

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