source: trunk/GSASII.py @ 1256

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

Added python version check; must be python 2.7!

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