source: trunk/GSASII.py @ 1838

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

fix problem reading TOF powder data

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