source: trunk/GSASII.py @ 1949

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

add image read for Rigaku R-Axis IV files

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