source: trunk/GSASII.py @ 1515

Last change on this file since 1515 was 1515, checked in by vondreele, 9 years ago

allow '#' comments in GSAS iparm files.
add class G2ColumnIDDialog for loading sample data for all histograms from a text file. This file has optional '#' comments followed by columns of data items each line begins with a file name that matches the file names for the histograms.
This class allows simple arithmetic modification of a column of data.
G2MultiChoiceDialog now allows selection of a block of items
Add 'Time' to Sample parameters - typically clock time
Further work on modulated structures
change default .gsa GSAS powder data extension to .gda - a lot more common.

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