source: trunk/GSASII.py @ 1364

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

read TOPAZ structure factor files

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