source: trunk/GSASII.py @ 1508

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

finish defining new defaultIparms.py & processing in ReadPowderIparm?

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