source: trunk/GSASII.py @ 1413

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

Add config support; default expressions

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