source: trunk/GSASII.py @ 1445

Last change on this file since 1445 was 1445, checked in by vondreele, 7 years ago

add error bars to calibration plot
disable Autosearch if peaks already picked
implement cell refine for TOF data - isn't quite right yet
implement tool tip on calibration plot to show position for each peak

  • 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-30 15:29:52 +0000 (Wed, 30 Jul 2014) $
6# $Author: vondreele $
7# $Revision: 1445 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1445 2014-07-30 15:29:52Z vondreele $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. 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: 1445 $")
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','difB','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,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]),0.0,G2IO.sfloat(s[2])])    #difC,difA,difB,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                ,{'peaks':[],'sigDict':{}})
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            ,{'peaks':[],'sigDict':{}})
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['DetDepthRef'] = False
2087                        else:
2088                            Data['type'] = 'PWDR'
2089                            Data['color'] = 'Paired'
2090                            Data['tilt'] = 0.0
2091                            Data['rotation'] = 0.0
2092                            Data['showLines'] = False
2093                            Data['ring'] = []
2094                            Data['rings'] = []
2095                            Data['cutoff'] = 10
2096                            Data['pixLimit'] = 20
2097                            Data['calibdmin'] = 0.5
2098                            Data['calibskip'] = 0
2099                            Data['edgemin'] = 100000000
2100                            Data['ellipses'] = []
2101                            Data['GonioAngles'] = [0.,0.,0.]
2102                            Data['DetDepth'] = 0.
2103                            Data['DetDepthRef'] = False
2104                            Data['calibrant'] = ''
2105                            Data['IOtth'] = [2.0,5.0]
2106                            Data['LRazimuth'] = [135,225]
2107                            Data['azmthOff'] = 0.0
2108                            Data['outChannels'] = 2500
2109                            Data['outAzimuths'] = 1
2110                            Data['centerAzm'] = False
2111                            Data['fullIntegrate'] = False
2112                            Data['setRings'] = False
2113                            Data['background image'] = ['',-1.0]                           
2114                            Data['dark image'] = ['',-1.0]
2115                        Data['setDefault'] = False
2116                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2117                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
2118                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2119                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2120                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2121                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2122                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
2123                        self.PickId = Id
2124                        self.Image = Id
2125                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!               
2126                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
2127        finally:
2128            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2129            os.chdir(path)
2130            dlg.Destroy()
2131
2132    def CheckNotebook(self):
2133        '''Make sure the data tree has the minimally expected controls.
2134        (BHT) correct?
2135        '''
2136        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2137            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2138            self.PatternTree.SetItemPyData(sub,[''])
2139        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2140            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2141            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2142        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2143            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2144            self.PatternTree.SetItemPyData(sub,{})
2145        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2146            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2147            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2148        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2149            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2150            self.PatternTree.SetItemPyData(sub,{})
2151        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2152            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2153            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2154                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2155               
2156    class CopyDialog(wx.Dialog):
2157        '''Creates a dialog for copying control settings between
2158        data tree items'''
2159        def __init__(self,parent,title,text,data):
2160            wx.Dialog.__init__(self,parent,-1,title, 
2161                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2162            self.data = data
2163            panel = wx.Panel(self)
2164            mainSizer = wx.BoxSizer(wx.VERTICAL)
2165            topLabl = wx.StaticText(panel,-1,text)
2166            mainSizer.Add((10,10),1)
2167            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2168            mainSizer.Add((10,10),1)
2169            ncols = len(data)/40+1
2170            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2171            for id,item in enumerate(self.data):
2172                ckbox = wx.CheckBox(panel,id,item[1])
2173                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2174                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2175            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2176            OkBtn = wx.Button(panel,-1,"Ok")
2177            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2178            cancelBtn = wx.Button(panel,-1,"Cancel")
2179            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2180            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2181            btnSizer.Add((20,20),1)
2182            btnSizer.Add(OkBtn)
2183            btnSizer.Add((20,20),1)
2184            btnSizer.Add(cancelBtn)
2185            btnSizer.Add((20,20),1)
2186           
2187            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2188            panel.SetSizer(mainSizer)
2189            panel.Fit()
2190            self.Fit()
2191       
2192        def OnCopyChange(self,event):
2193            id = event.GetId()
2194            self.data[id][0] = self.FindWindowById(id).GetValue()       
2195           
2196        def OnOk(self,event):
2197            parent = self.GetParent()
2198            parent.Raise()
2199            self.EndModal(wx.ID_OK)             
2200           
2201        def OnCancel(self,event):
2202            parent = self.GetParent()
2203            parent.Raise()
2204            self.EndModal(wx.ID_CANCEL)             
2205           
2206        def GetData(self):
2207            return self.data
2208       
2209    class SumDialog(wx.Dialog):
2210        'Allows user to supply scale factor(s) when summing data'
2211        def __init__(self,parent,title,text,dataType,data):
2212            wx.Dialog.__init__(self,parent,-1,title, 
2213                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2214            self.data = data
2215            panel = wx.Panel(self)
2216            mainSizer = wx.BoxSizer(wx.VERTICAL)
2217            topLabl = wx.StaticText(panel,-1,text)
2218            mainSizer.Add((10,10),1)
2219            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2220            mainSizer.Add((10,10),1)
2221            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2222            for id,item in enumerate(self.data[:-1]):
2223                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2224                name.SetEditable(False)
2225                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2226                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2227                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2228                dataGridSizer.Add(scale,0,wx.LEFT,10)
2229                dataGridSizer.Add(name,0,wx.RIGHT,10)
2230            if dataType:
2231                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2232                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2233                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2234                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2235                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2236                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2237            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2238            OkBtn = wx.Button(panel,-1,"Ok")
2239            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2240            cancelBtn = wx.Button(panel,-1,"Cancel")
2241            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2242            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2243            btnSizer.Add((20,20),1)
2244            btnSizer.Add(OkBtn)
2245            btnSizer.Add((20,20),1)
2246            btnSizer.Add(cancelBtn)
2247            btnSizer.Add((20,20),1)
2248           
2249            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2250            panel.SetSizer(mainSizer)
2251            panel.Fit()
2252            self.Fit()
2253
2254        def OnScaleChange(self,event):
2255            id = event.GetId()
2256            value = self.FindWindowById(id).GetValue()
2257            try:
2258                self.data[id][0] = float(value)
2259                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2260            except ValueError:
2261                if value and '-' not in value[0]:
2262                    print 'bad input - numbers only'
2263                    self.FindWindowById(id).SetValue('0.000')
2264           
2265        def OnNameChange(self,event):
2266            self.data[-1] = self.name.GetValue() 
2267           
2268        def OnOk(self,event):
2269            parent = self.GetParent()
2270            parent.Raise()
2271            self.EndModal(wx.ID_OK)             
2272           
2273        def OnCancel(self,event):
2274            parent = self.GetParent()
2275            parent.Raise()
2276            self.EndModal(wx.ID_CANCEL)             
2277           
2278        def GetData(self):
2279            return self.data
2280                       
2281    def OnPwdrSum(self,event):
2282        'Sum together powder data(?)'
2283        TextList = []
2284        DataList = []
2285        SumList = []
2286        Names = []
2287        Inst = None
2288        SumItemList = []
2289        Comments = ['Sum equals: \n']
2290        if self.PatternTree.GetCount():
2291            item, cookie = self.PatternTree.GetFirstChild(self.root)
2292            while item:
2293                name = self.PatternTree.GetItemText(item)
2294                Names.append(name)
2295                if 'PWDR' in name:
2296                    TextList.append([0.0,name])
2297                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2298                    if not Inst:
2299                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2300                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2301            if len(TextList) < 2:
2302                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2303                return
2304            TextList.append('default_sum_name')               
2305            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2306            try:
2307                if dlg.ShowModal() == wx.ID_OK:
2308                    lenX = 0
2309                    Xminmax = [0,0]
2310                    Xsum = []
2311                    Ysum = []
2312                    Vsum = []
2313                    result = dlg.GetData()
2314                    for i,item in enumerate(result[:-1]):
2315                        scale,name = item
2316                        data = DataList[i]
2317                        if scale:
2318                            Comments.append("%10.3f %s" % (scale,' * '+name))
2319                            x,y,w,yc,yb,yd = data   #numpy arrays!
2320                            v = 1./w
2321                            if lenX:
2322                                if lenX != len(x):
2323                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2324                                        '\nExpected:'+str(lenX)+ \
2325                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2326                                    return
2327                            else:
2328                                lenX = len(x)
2329                            if Xminmax[1]:
2330                                if Xminmax != [x[0],x[-1]]:
2331                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2332                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2333                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2334                                    return
2335                                else:
2336                                    for j,yi in enumerate(y):
2337                                         Ysum[j] += scale*yi
2338                                         Vsum[j] += abs(scale)*v[j]
2339                            else:
2340                                Xminmax = [x[0],x[-1]]
2341                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2342                                for j,yi in enumerate(y):
2343                                    Xsum.append(x[j])
2344                                    Ysum.append(scale*yi)
2345                                    Vsum.append(abs(scale*v[j]))
2346                    Wsum = 1./np.array(Vsum)
2347                    outname = 'PWDR '+result[-1]
2348                    Id = 0
2349                    if outname in Names:
2350                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2351                        try:
2352                            if dlg2.ShowModal() == wx.ID_OK:
2353                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2354                                self.PatternTree.Delete(Id)
2355                        finally:
2356                            dlg2.Destroy()
2357                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2358                    if Id:
2359                        Sample = G2pdG.SetDefaultSample()
2360                        valuesdict = {
2361                            'wtFactor':1.0,
2362                            'Dummy':False,
2363                            'ranId':ran.randint(0,sys.maxint),
2364                            }
2365                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2366                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2367                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2368                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2369                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2370                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2371                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2372                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2373                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2374                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2375                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2376                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2377                        self.PatternTree.SelectItem(Id)
2378                        self.PatternTree.Expand(Id)
2379            finally:
2380                dlg.Destroy()
2381
2382    def OnImageSum(self,event):
2383        'Sum together image data(?)'
2384        TextList = []
2385        DataList = []
2386        SumList = []
2387        Names = []
2388        Inst = []
2389        SumItemList = []
2390        Comments = ['Sum equals: \n']
2391        if self.PatternTree.GetCount():
2392            item, cookie = self.PatternTree.GetFirstChild(self.root)
2393            while item:
2394                name = self.PatternTree.GetItemText(item)
2395                Names.append(name)
2396                if 'IMG' in name:
2397                    TextList.append([0.0,name])
2398                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2399                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2400                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2401            if len(TextList) < 2:
2402                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2403                return
2404            TextList.append('default_sum_name')               
2405            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2406            try:
2407                if dlg.ShowModal() == wx.ID_OK:
2408                    imSize = 0
2409                    result = dlg.GetData()
2410                    First = True
2411                    Found = False
2412                    for i,item in enumerate(result[:-1]):
2413                        scale,name = item
2414                        data = DataList[i]
2415                        if scale:
2416                            Found = True                               
2417                            Comments.append("%10.3f %s" % (scale,' * '+name))
2418                            Npix,imagefile = data
2419                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2420                            if First:
2421                                newImage = np.zeros_like(image)
2422                                First = False
2423                            if imSize:
2424                                if imSize != Npix:
2425                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2426                                        '\nExpected:'+str(imSize)+ \
2427                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2428                                    return
2429                                newImage = newImage+scale*image
2430                            else:
2431                                imSize = Npix
2432                                newImage = newImage+scale*image
2433                            del(image)
2434                    if not Found:
2435                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2436                        return
2437                       
2438                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2439                    outname = 'IMG '+result[-1]
2440                    Id = 0
2441                    if outname in Names:
2442                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2443                        try:
2444                            if dlg2.ShowModal() == wx.ID_OK:
2445                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2446                        finally:
2447                            dlg2.Destroy()
2448                    else:
2449                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2450                    if Id:
2451                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2452                            'G2img files (*.G2img)|*.G2img', 
2453                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2454                        if dlg.ShowModal() == wx.ID_OK:
2455                            newimagefile = dlg.GetPath()
2456                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2457                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2458                            Imax = np.amax(newImage)
2459                            Imin = np.amin(newImage)
2460                            newImage = []
2461                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2462                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2463                        del(newImage)
2464                        if self.imageDefault:
2465                            Data = copy.copy(self.imageDefault)
2466                        Data['showLines'] = True
2467                        Data['ring'] = []
2468                        Data['rings'] = []
2469                        Data['cutoff'] = 10
2470                        Data['pixLimit'] = 20
2471                        Data['ellipses'] = []
2472                        Data['calibrant'] = ''
2473                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2474                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2475                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2476                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2477                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2478                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2479                        self.PatternTree.SelectItem(Id)
2480                        self.PatternTree.Expand(Id)
2481                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2482                        self.Image = self.PickId
2483            finally:
2484                dlg.Destroy()
2485                     
2486    def OnAddPhase(self,event):
2487        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2488        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2489            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2490        else:
2491            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2492        PhaseName = ''
2493        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2494            style=wx.OK)
2495        if dlg.ShowModal() == wx.ID_OK:
2496            PhaseName = dlg.GetValue()
2497        dlg.Destroy()
2498        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2499        E,SGData = G2spc.SpcGroup('P 1')
2500        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2501       
2502    def OnDeletePhase(self,event):
2503        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2504        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2505        if self.dataFrame:
2506            self.dataFrame.Clear() 
2507        TextList = []
2508        DelList = []
2509        DelItemList = []
2510        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2511            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2512        else:
2513            return
2514        if sub:
2515            item, cookie = self.PatternTree.GetFirstChild(sub)
2516            while item:
2517                TextList.append(self.PatternTree.GetItemText(item))
2518                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2519            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2520            try:
2521                if dlg.ShowModal() == wx.ID_OK:
2522                    result = dlg.GetSelections()
2523                    for i in result: DelList.append([i,TextList[i]])
2524                    item, cookie = self.PatternTree.GetFirstChild(sub)
2525                    i = 0
2526                    while item:
2527                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2528                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2529                        i += 1
2530                    for item in DelItemList:
2531                        name = self.PatternTree.GetItemText(item)
2532                        self.PatternTree.Delete(item)
2533                        self.G2plotNB.Delete(name)
2534                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2535                    while item:
2536                        name = self.PatternTree.GetItemText(item)
2537                        if 'PWDR' in name:
2538                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2539                            refList = self.PatternTree.GetItemPyData(Id)
2540                            for i,item in DelList:
2541                                del(refList[item])
2542                            self.PatternTree.SetItemPyData(Id,refList)
2543                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2544            finally:
2545                dlg.Destroy()
2546               
2547    def OnRenameData(self,event):
2548        'Renames an existing phase. Called by Data/Rename Phase menu'
2549        name = self.PatternTree.GetItemText(self.PickId)     
2550        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2551            dataType = name[:name.index(' ')+1]                 #includes the ' '
2552            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2553                defaultValue=name[name.index(' ')+1:])
2554            try:
2555                if dlg.ShowModal() == wx.ID_OK:
2556                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2557            finally:
2558                dlg.Destroy()
2559       
2560    def GetFileList(self,fileType,skip=None):        #potentially useful?
2561        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2562        fileList = []
2563        Source = ''
2564        id, cookie = self.PatternTree.GetFirstChild(self.root)
2565        while id:
2566            name = self.PatternTree.GetItemText(id)
2567            if fileType in name:
2568                if id == skip:
2569                    Source = name
2570                else:
2571                    fileList.append([False,name,id])
2572            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2573        if skip:
2574            return fileList,Source
2575        else:
2576            return fileList
2577           
2578    def OnDataDelete(self, event):
2579        '''Delete one or more histograms from data tree. Called by the
2580        Data/DeleteData menu
2581        '''
2582        TextList = ['All Data']
2583        DelList = []
2584        DelItemList = []
2585        ifPWDR = False
2586        ifSASD = False
2587        ifIMG = False
2588        ifHKLF = False
2589        ifPDF = False
2590        if self.PatternTree.GetCount():
2591            item, cookie = self.PatternTree.GetFirstChild(self.root)
2592            while item:
2593                name = self.PatternTree.GetItemText(item)
2594                if name not in ['Notebook','Controls','Covariance','Constraints',
2595                    'Restraints','Phases','Rigid bodies']:
2596                    if 'PWDR' in name: ifPWDR = True
2597                    if 'SASD' in name: ifSASD = True
2598                    if 'IMG' in name: ifIMG = True
2599                    if 'HKLF' in name: ifHKLF = True
2600                    if 'PDF' in name: ifPDF = True
2601                    TextList.append(name)
2602                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2603            if ifPWDR: TextList.insert(1,'All PWDR')
2604            if ifSASD: TextList.insert(1,'All SASD')
2605            if ifIMG: TextList.insert(1,'All IMG')
2606            if ifHKLF: TextList.insert(1,'All HKLF')
2607            if ifPDF: TextList.insert(1,'All PDF')               
2608            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2609            try:
2610                if dlg.ShowModal() == wx.ID_OK:
2611                    result = dlg.GetSelections()
2612                    for i in result: DelList.append(TextList[i])
2613                    if 'All Data' in DelList:
2614                        DelList = [item for item in TextList if item[:3] != 'All']
2615                    elif 'All PWDR' in DelList:
2616                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2617                    elif 'All SASD' in DelList:
2618                        DelList = [item for item in TextList if item[:4] == 'SASD']
2619                    elif 'All IMG' in DelList:
2620                        DelList = [item for item in TextList if item[:3] == 'IMG']
2621                    elif 'All HKLF' in DelList:
2622                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2623                    elif 'All PDF' in DelList:
2624                        DelList = [item for item in TextList if item[:3] == 'PDF']
2625                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2626                    while item:
2627                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2628                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2629                    for item in DelItemList:
2630                        self.PatternTree.Delete(item)
2631                    self.PickId = 0
2632                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2633            finally:
2634                dlg.Destroy()
2635
2636    def OnFileOpen(self, event, filename=None):
2637        '''Reads in a GSAS-II .gpx project file in response to the
2638        File/Open Project menu button
2639        '''
2640        result = wx.ID_OK
2641        Id = 0
2642        self.EnablePlot = False
2643        if self.PatternTree.GetChildrenCount(self.root,False):
2644            if self.dataFrame:
2645                self.dataFrame.Clear() 
2646            dlg = wx.MessageDialog(
2647                self,
2648                'Do you want to overwrite the current project? '
2649                'Any unsaved changes will be lost. Press OK to continue.',
2650                'Overwrite?',  wx.OK | wx.CANCEL)
2651            try:
2652                result = dlg.ShowModal()
2653                if result == wx.ID_OK:
2654                    self.PatternTree.DeleteChildren(self.root)
2655                    self.GSASprojectfile = ''
2656                    if self.HKL: self.HKL = []
2657                    if self.G2plotNB.plotList:
2658                        self.G2plotNB.clear()
2659            finally:
2660                dlg.Destroy()
2661        if result != wx.ID_OK: return
2662
2663        if not filename:
2664            if self.dataDisplay: self.dataDisplay.Destroy()
2665            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2666                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2667            try:
2668                if dlg.ShowModal() != wx.ID_OK: return
2669                self.GSASprojectfile = dlg.GetPath()
2670                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2671                self.dirname = dlg.GetDirectory()
2672            finally:
2673                dlg.Destroy()
2674        else:
2675            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2676            self.dirname = os.path.split(filename)[0]
2677
2678        G2IO.ProjFileOpen(self)
2679        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2680        self.PatternTree.Expand(self.root)
2681        self.HKL = []
2682        item, cookie = self.PatternTree.GetFirstChild(self.root)
2683        while item and not Id:
2684            name = self.PatternTree.GetItemText(item)
2685            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
2686                Id = item
2687            elif name == 'Controls':
2688                data = self.PatternTree.GetItemPyData(item)
2689                if data:
2690                    for item in self.Refine: item.Enable(True)
2691                    self.EnableSeqRefineMenu()
2692            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2693        if Id:
2694            self.EnablePlot = True
2695            self.PatternTree.SelectItem(Id)
2696        self.CheckNotebook()
2697        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2698
2699    def OnFileClose(self, event):
2700        '''Clears the data tree in response to the
2701        File/New Project menu button. User is given option to save
2702        the project.
2703        '''
2704        if self.dataFrame:
2705            self.dataFrame.Clear()
2706            self.dataFrame.SetLabel('GSAS-II data display') 
2707        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2708        try:
2709            result = dlg.ShowModal()
2710            if result == wx.ID_OK:
2711                self.OnFileSaveMenu(event)
2712            if result != wx.ID_CANCEL:
2713                self.GSASprojectfile = ''
2714                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2715                self.PatternTree.DeleteChildren(self.root)
2716                if self.HKL: self.HKL = []
2717                if self.G2plotNB.plotList:
2718                    self.G2plotNB.clear()
2719        finally:
2720            dlg.Destroy()
2721
2722    def OnFileSave(self, event):
2723        '''Save the current project in response to the
2724        File/Save Project menu button
2725        '''
2726       
2727        if self.GSASprojectfile: 
2728            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2729            G2IO.ProjFileSave(self)
2730        else:
2731            self.OnFileSaveas(event)
2732
2733    def OnFileSaveas(self, event):
2734        '''Save the current project in response to the
2735        File/Save as menu button
2736        '''
2737        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2738            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2739        try:
2740            if dlg.ShowModal() == wx.ID_OK:
2741                self.GSASprojectfile = dlg.GetPath()
2742                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2743                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2744                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2745                G2IO.ProjFileSave(self)
2746                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2747        finally:
2748            dlg.Destroy()
2749
2750    def ExitMain(self, event):
2751        '''Called if the main window is closed'''
2752        if self.undofile:
2753            os.remove(self.undofile)
2754        sys.exit()
2755       
2756    def OnFileExit(self, event):
2757        '''Called in response to the File/Quit menu button'''
2758        if self.dataFrame:
2759            self.dataFrame.Clear() 
2760            self.dataFrame.Destroy()
2761        self.Close()
2762       
2763    # def OnExportPatterns(self,event):
2764    #     names = ['All']
2765    #     exports = []
2766    #     item, cookie = self.PatternTree.GetFirstChild(self.root)
2767    #     while item:
2768    #         name = self.PatternTree.GetItemText(item)
2769    #         if 'PWDR' in name:
2770    #             names.append(name)
2771    #         item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2772    #     if names:
2773    #         dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2774    #         if dlg.ShowModal() == wx.ID_OK:
2775    #             sel = dlg.GetSelections()
2776    #             if sel[0] == 0:
2777    #                 exports = names[1:]
2778    #             else:
2779    #                 for x in sel:
2780    #                     exports.append(names[x])
2781    #         dlg.Destroy()
2782    #     if exports:
2783    #         dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '',
2784    #             'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2785    #             wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2786    #         try:
2787    #             if dlg.ShowModal() == wx.ID_OK:
2788    #                 powderfile = dlg.GetPath()
2789    #                 powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2790    #                 if 'fxye' in powderfile:
2791    #                     G2IO.powderFxyeSave(self,exports,powderfile)
2792    #                 else:       #just xye
2793    #                     G2IO.powderXyeSave(self,exports,powderfile)
2794    #         finally:
2795    #             dlg.Destroy()
2796       
2797    def OnExportPeakList(self,event):
2798        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2799            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2800        try:
2801            if dlg.ShowModal() == wx.ID_OK:
2802                self.peaklistfile = dlg.GetPath()
2803                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2804                file = open(self.peaklistfile,'w')               
2805                item, cookie = self.PatternTree.GetFirstChild(self.root)
2806                while item:
2807                    name = self.PatternTree.GetItemText(item)
2808                    if 'PWDR' in name:
2809                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2810                        while item2:
2811                            name2 = self.PatternTree.GetItemText(item2)
2812                            if name2 == 'Peak List':
2813                                peaks = self.PatternTree.GetItemPyData(item2)['peaks']
2814                                file.write("%s \n" % (name+' Peak List'))               
2815                                for peak in peaks:
2816                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2817                                        (peak[0],peak[2],peak[4],peak[6]))
2818                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2819                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2820                file.close()
2821        finally:
2822            dlg.Destroy()
2823       
2824    def OnExportHKL(self,event):
2825        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2826            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2827        try:
2828            if dlg.ShowModal() == wx.ID_OK:
2829                self.peaklistfile = dlg.GetPath()
2830                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2831                file = open(self.peaklistfile,'w')               
2832                item, cookie = self.PatternTree.GetFirstChild(self.root)
2833                while item:
2834                    name = self.PatternTree.GetItemText(item)
2835                    if 'PWDR' in name:
2836                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2837                        while item2:
2838                            name2 = self.PatternTree.GetItemText(item2)
2839                            if name2 == 'Reflection Lists':
2840                                data = self.PatternTree.GetItemPyData(item2)
2841                                phases = data.keys()
2842                                for phase in phases:
2843                                    peaks = data[phase]
2844                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2845                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2846                                    for peak in peaks:
2847                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2848                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2849                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2850                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2851                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2852                file.close()
2853        finally:
2854            dlg.Destroy()
2855       
2856    def OnExportPDF(self,event):
2857        #need S(Q) and G(R) to be saved here - probably best from selection?
2858        names = ['All']
2859        exports = []
2860        item, cookie = self.PatternTree.GetFirstChild(self.root)
2861        while item:
2862            name = self.PatternTree.GetItemText(item)
2863            if 'PDF' in name:
2864                names.append(name)
2865            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2866        if names:
2867            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2868            if dlg.ShowModal() == wx.ID_OK:
2869                sel = dlg.GetSelections()
2870                if sel[0] == 0:
2871                    exports = names[1:]
2872                else:
2873                    for x in sel:
2874                        exports.append(names[x])
2875            dlg.Destroy()
2876        if exports:
2877            G2IO.PDFSave(self,exports)
2878       
2879    def OnMakePDFs(self,event):
2880        '''Calculates PDFs
2881        '''
2882        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2883        TextList = ['All PWDR']
2884        PDFlist = []
2885        Names = []
2886        if self.PatternTree.GetCount():
2887            id, cookie = self.PatternTree.GetFirstChild(self.root)
2888            while id:
2889                name = self.PatternTree.GetItemText(id)
2890                Names.append(name)
2891                if 'PWDR' in name:
2892                    TextList.append(name)
2893                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2894            if len(TextList) == 1:
2895                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2896                return
2897            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2898            try:
2899                if dlg.ShowModal() == wx.ID_OK:
2900                    result = dlg.GetSelections()
2901                    for i in result: PDFlist.append(TextList[i])
2902                    if 0 in result:
2903                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2904                    for item in PDFlist:
2905                        PWDRname = item[4:]
2906                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2907                        Data = {
2908                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2909                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2910                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2911                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2912                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2913                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2914                            'Lorch':True,}
2915                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2916                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2917                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2918                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2919                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2920                for item in self.ExportPDF: item.Enable(True)
2921            finally:
2922                dlg.Destroy()
2923               
2924    def GetPWDRdatafromTree(self,PWDRname):
2925        ''' Returns powder data from GSASII tree
2926
2927        :param str PWDRname: a powder histogram name as obtained from
2928          :meth:`GSASIIstruct.GetHistogramNames`
2929
2930        :returns: PWDRdata = powder data dictionary with
2931          Powder data arrays, Limits, Instrument Parameters,
2932          Sample Parameters           
2933        '''
2934        PWDRdata = {}
2935        try:
2936            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2937        except ValueError:
2938            PWDRdata['wtFactor'] = 1.0
2939        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2940        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2941        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2942        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2943        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2944        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2945        if 'ranId' not in PWDRdata:  # patch, add a random Id
2946            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
2947        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
2948            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
2949        return PWDRdata
2950
2951    def GetHKLFdatafromTree(self,HKLFname):
2952        ''' Returns single crystal data from GSASII tree
2953
2954        :param str HKLFname: a single crystal histogram name as obtained
2955          from
2956          :meth:`GSASIIstruct.GetHistogramNames`
2957
2958        :returns: HKLFdata = single crystal data list of reflections
2959
2960        '''
2961        HKLFdata = {}
2962        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2963#        try:
2964#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2965#        except ValueError:
2966#            HKLFdata['wtFactor'] = 1.0
2967        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2968        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2969        return HKLFdata
2970       
2971    def GetPhaseData(self):
2972        '''Returns a dict with defined phases.
2973        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
2974        get same info from GPX file.
2975        '''
2976        phaseData = {}
2977        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2978            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2979        else:
2980            print 'no phases found in GetPhaseData'
2981            sub = None
2982        if sub:
2983            item, cookie = self.PatternTree.GetFirstChild(sub)
2984            while item:
2985                phaseName = self.PatternTree.GetItemText(item)
2986                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2987                if 'ranId' not in phaseData[phaseName]:
2988                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2989                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2990        return phaseData
2991
2992    def GetPhaseInfofromTree(self):
2993        '''Get the phase names and their rId values,
2994        also the histograms used in each phase.
2995
2996        :returns: (phaseRIdList, usedHistograms) where
2997
2998          * phaseRIdList is a list of random Id values for each phase
2999          * usedHistograms is a dict where the keys are the phase names
3000            and the values for each key are a list of the histogram names
3001            used in each phase.
3002        '''
3003        phaseRIdList = []
3004        usedHistograms = {}
3005        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3006        if sub:
3007            item, cookie = self.PatternTree.GetFirstChild(sub)
3008            while item:
3009                phaseName = self.PatternTree.GetItemText(item)
3010                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3011                if ranId: phaseRIdList.append(ranId)
3012                data = self.PatternTree.GetItemPyData(item)
3013                UseList = data['Histograms']
3014                usedHistograms[phaseName] = UseList.keys()
3015                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3016        return phaseRIdList,usedHistograms
3017
3018    def GetPhaseNames(self):
3019        '''Returns a list of defined phases.
3020        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3021        get same info from GPX file.
3022        '''
3023        phaseNames = []
3024        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3025            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3026        else:
3027            print 'no phases found in GetPhaseNames'
3028            sub = None
3029        if sub:
3030            item, cookie = self.PatternTree.GetFirstChild(sub)
3031            while item:
3032                phase = self.PatternTree.GetItemText(item)
3033                phaseNames.append(phase)
3034                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3035        return phaseNames
3036   
3037    def GetHistogramNames(self,hType):
3038        """ Returns a list of histogram names found in the GSASII data tree
3039        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3040        get same info from GPX file.
3041       
3042        :param str hType: list of histogram types
3043        :return: list of histogram names
3044       
3045        """
3046        HistogramNames = []
3047        if self.PatternTree.GetCount():
3048            item, cookie = self.PatternTree.GetFirstChild(self.root)
3049            while item:
3050                name = self.PatternTree.GetItemText(item)
3051                if name[:4] in hType:
3052                    HistogramNames.append(name)       
3053                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3054
3055        return HistogramNames
3056
3057                   
3058    def GetUsedHistogramsAndPhasesfromTree(self):
3059        ''' Returns all histograms that are found in any phase
3060        and any phase that uses a histogram.
3061        This also assigns numbers to used phases and histograms by the
3062        order they appear in the file.
3063        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3064        get same info from GPX file.
3065
3066        :returns: (Histograms,Phases)
3067
3068            * Histograms = dictionary of histograms as {name:data,...}
3069            * Phases = dictionary of phases that use histograms
3070        '''
3071        Histograms = {}
3072        Phases = {}
3073        phaseNames = self.GetPhaseNames()
3074        phaseData = self.GetPhaseData()
3075        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3076
3077        for phase in phaseData:
3078            Phase = phaseData[phase]
3079            pId = phaseNames.index(phase)
3080            Phase['pId'] = pId
3081            if Phase['Histograms']:
3082                if phase not in Phases:
3083                    Phases[phase] = Phase
3084                for hist in Phase['Histograms']:
3085                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3086                        Phase['Histograms'][hist]['Use'] = True         
3087                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3088                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3089                        if item:
3090                            if 'PWDR' in hist[:4]: 
3091                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3092                            elif 'HKLF' in hist[:4]:
3093                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3094                            hId = histoList.index(hist)
3095                            Histograms[hist]['hId'] = hId
3096                        else: # would happen if a referenced histogram were renamed or deleted
3097                            print('For phase "'+str(phase)+
3098                                  '" unresolved reference to histogram "'+str(hist)+'"')
3099        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3100        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3101        return Histograms,Phases
3102       
3103    def MakeLSParmDict(self):
3104        '''Load all parameters used for computation from the tree into a
3105        dict of paired values [value, refine flag]. Note that this is
3106        different than the parmDict used in the refinement, which only has
3107        values.
3108
3109        Note that similar things are done in
3110        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3111        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3112        a GPX file).
3113
3114        :returns: (parmDict,varyList) where:
3115
3116         * parmDict is a dict with values and refinement flags
3117           for each parameter and
3118         * varyList is a list of variables (refined parameters).
3119        '''
3120        parmDict = {}
3121        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3122        for phase in Phases:
3123            if 'pId' not in Phases[phase]:
3124                self.ErrorDialog('View parameter error','You must run least squares at least once')
3125                raise Exception,'No pId for phase '+str(phase)
3126        rigidbodyDict = self.PatternTree.GetItemPyData(   
3127            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3128        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3129        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3130        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3131        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3132        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3133        varyList = rbVary+phaseVary+hapVary+histVary
3134        parmDict.update(rbDict)
3135        parmDict.update(phaseDict)
3136        parmDict.update(hapDict)
3137        parmDict.update(histDict)
3138        for parm in parmDict:
3139            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3140                'Omega','Chi','Phi','nDebye','nPeaks']:
3141                parmDict[parm] = [parmDict[parm],'-']
3142            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3143                parmDict[parm] = [parmDict[parm],'-']
3144            elif parm in varyList:
3145                parmDict[parm] = [parmDict[parm],'T']
3146            else:
3147                parmDict[parm] = [parmDict[parm],'F']
3148        # for i in parmDict: print i,'\t',parmDict[i]
3149        # fl = open('parmDict.dat','wb')
3150        # import cPickle
3151        # cPickle.dump(parmDict,fl,1)
3152        # fl.close()
3153        return parmDict,varyList
3154
3155    def ShowLSParms(self,event):
3156        '''Displays a window showing all parameters in the refinement.
3157        Called from the Calculate/View LS Parms menu.
3158        '''
3159        parmDict,varyList = self.MakeLSParmDict()
3160        parmValDict = {}
3161        for i in parmDict:
3162            parmValDict[i] = parmDict[i][0]
3163           
3164        reqVaryList = tuple(varyList) # save requested variables
3165        try:
3166            # process constraints
3167            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3168            Constraints = self.PatternTree.GetItemPyData(sub)
3169            constList = []
3170            for item in Constraints:
3171                if item.startswith('_'): continue
3172                constList += Constraints[item]
3173            G2mv.InitVars()
3174            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3175            groups,parmlist = G2mv.GroupConstraints(constrDict)
3176            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3177            G2mv.Map2Dict(parmValDict,varyList)
3178        except:
3179            pass
3180        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3181        dlg.ShowModal()
3182        dlg.Destroy()
3183       
3184    def OnRefine(self,event):
3185        '''Perform a refinement.
3186        Called from the Calculate/Refine menu.
3187        '''       
3188        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3189        if Id:
3190            dlg = wx.MessageDialog(
3191                self,
3192                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3193                'Remove sequential results?',wx.OK|wx.CANCEL)
3194            if dlg.ShowModal() == wx.ID_OK:
3195                self.PatternTree.Delete(Id)
3196                dlg.Destroy()
3197            else:
3198                dlg.Destroy()
3199                return
3200
3201        self.OnFileSave(event)
3202        # check that constraints are OK here
3203        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3204        if errmsg:
3205            print('Error in constraints:\n'+errmsg+
3206                  '\nRefinement not possible')
3207            self.ErrorDialog('Constraint Error',
3208                             'Error in constraints:\n'+errmsg+
3209                             '\nRefinement not possible')
3210            return
3211        if warnmsg:
3212            print('Conflict between refinment flag settings and constraints:\n'+
3213                  warnmsg+'\nRefinement not possible')
3214            self.ErrorDialog('Refinement Flag Error',
3215                             'Conflict between refinment flag settings and constraints:\n'+
3216                             warnmsg+
3217                             '\nRefinement not possible')
3218            return
3219        #works - but it'd be better if it could restore plots
3220        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3221            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3222        screenSize = wx.ClientDisplayRect()
3223        Size = dlg.GetSize()
3224        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3225        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3226        dlg.SetSize(Size)
3227        Rw = 100.00
3228        try:
3229            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3230        finally:
3231            dlg.Destroy()
3232        oldId =  self.PatternTree.GetSelection()        #retain current selection
3233        oldName = self.PatternTree.GetItemText(oldId)
3234        parentId = self.PatternTree.GetItemParent(oldId)
3235        parentName = ''
3236        if parentId:
3237            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3238        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3239        try:
3240            if dlg.ShowModal() == wx.ID_OK:
3241                Id = 0
3242                self.PatternTree.DeleteChildren(self.root)
3243                if self.HKL: self.HKL = []
3244                if self.G2plotNB.plotList:
3245                    self.G2plotNB.clear()
3246                G2IO.ProjFileOpen(self)
3247                item, cookie = self.PatternTree.GetFirstChild(self.root)
3248                while item and not Id:
3249                    name = self.PatternTree.GetItemText(item)
3250                    if name[:4] in ['PWDR','HKLF']:
3251                        Id = item
3252                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3253                if Id:
3254                    self.PatternTree.SelectItem(Id)
3255                if parentName:
3256                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3257                    if parentId:
3258                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3259                    else:
3260                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3261                    self.PatternTree.SelectItem(itemId)
3262        finally:
3263            dlg.Destroy()
3264
3265    def OnSeqRefine(self,event):
3266        '''Perform a sequential refinement.
3267        Called from the Calculate/Sequential refine menu.
3268        '''       
3269        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3270        if not Id:
3271            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3272            self.PatternTree.SetItemPyData(Id,{})           
3273        self.OnFileSave(event)
3274        # check that constraints are OK here
3275        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3276        if errmsg:
3277            print('Error in constraints:\n'+errmsg+
3278                  '\nRefinement not possible')
3279            self.ErrorDialog('Constraint Error',
3280                             'Error in constraints:\n'+errmsg+
3281                             '\nRefinement not possible')
3282            return
3283        if warnmsg:
3284            print('Conflict between refinment flag settings and constraints:\n'+
3285                  warnmsg+'\nRefinement not possible')
3286            self.ErrorDialog('Refinement Flag Error',
3287                             'Conflict between refinment flag settings and constraints:\n'+
3288                             warnmsg+'\nRefinement not possible')
3289            return
3290        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3291            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3292        screenSize = wx.ClientDisplayRect()
3293        Size = dlg.GetSize()
3294        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3295        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3296        dlg.SetSize(Size)
3297        try:
3298            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3299        finally:
3300            dlg.Destroy()       
3301        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3302        try:
3303            if dlg.ShowModal() == wx.ID_OK:
3304                Id = 0
3305                self.PatternTree.DeleteChildren(self.root)
3306                if self.HKL: self.HKL = []
3307                if self.G2plotNB.plotList:
3308                    self.G2plotNB.clear()
3309                G2IO.ProjFileOpen(self)
3310                Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3311                self.PatternTree.SelectItem(Id)
3312
3313        finally:
3314            dlg.Destroy()
3315       
3316    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3317        'Display an error message'
3318        result = None
3319        if parent is None:
3320            dlg = wx.MessageDialog(self, message, title,  wtype)
3321        else:
3322            dlg = wx.MessageDialog(parent, message, title,  wtype)
3323            dlg.CenterOnParent() # not working on Mac
3324        try:
3325            result = dlg.ShowModal()
3326        finally:
3327            dlg.Destroy()
3328        return result
3329
3330class GSASIImain(wx.App):
3331    '''Defines a wxApp for GSAS-II
3332
3333    Creates a wx frame (self.main) which contains the display of the
3334    data tree.
3335    '''
3336    def OnInit(self):
3337        '''Called automatically when the app is created.'''
3338        if '2.7' not in sys.version[:5]:
3339            dlg = wx.MessageDialog(None, 
3340                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version[:5],
3341                'Python version error',  wx.OK)
3342            try:
3343                result = dlg.ShowModal()
3344            finally:
3345                dlg.Destroy()
3346            sys.exit()
3347        self.main = GSASII(None)
3348        self.main.Show()
3349        self.SetTopWindow(self.main)
3350        # DEBUG: jump to sequential results
3351        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3352        #self.main.PatternTree.SelectItem(Id)
3353        # end DEBUG
3354        return True
3355    # def MacOpenFile(self, filename):
3356    #     '''Called on Mac every time a file is dropped on the app when it is running,
3357    #     treat this like a File/Open project menu action.
3358    #     Should be ignored on other platforms
3359    #     '''
3360    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3361    #     print 'MacOpen',filename
3362    #     if os.path.splitext(filename)[1] == '.py': return
3363    #     # end PATCH
3364    #     self.main.OnFileOpen(None,filename)
3365    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3366   
3367def main():
3368    '''Start up the GSAS-II application'''
3369    #application = GSASIImain() # don't redirect output, someday we
3370    # may want to do this if we can
3371    application = GSASIImain(0)
3372    if wxInspector:
3373        import wx.lib.inspection as wxeye
3374        wxeye.InspectionTool().Show()
3375
3376    #application.main.OnRefine(None)
3377    application.MainLoop()
3378   
3379if __name__ == '__main__':
3380    # print versions
3381    print "Python module versions loaded:"
3382    print "python:     ",sys.version[:5]
3383    print "wxpython:   ",wx.__version__
3384    print "matplotlib: ",mpl.__version__
3385    print "numpy:      ",np.__version__
3386    print "scipy:      ",sp.__version__
3387    print "OpenGL:     ",ogl.__version__
3388    try:
3389        import Image
3390        print "Image (PIL):",Image.VERSION
3391    except ImportError:
3392        try:
3393            from PIL import Image
3394            print "pillow:     ",Image.VERSION
3395        except ImportError:
3396            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3397    try:
3398        import mkl
3399        print "Max threads ",mkl.get_max_threads()
3400    except:
3401        pass
3402    print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
3403    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.