source: trunk/GSASII.py @ 1573

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

missing import traceback; real error on Super=None now fixed
messed up import phase; Data/Add? phase seems OK in wx 3.0.0.0

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