source: trunk/GSASII.py @ 1500

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

Improve drag/drop - put terse instruction in tree window status bar & eliminate error when dragging item to end of tree.

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