source: trunk/GSASII.py @ 1605

Last change on this file since 1605 was 1605, checked in by toby, 7 years ago

add code to prevent blank phase names

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