source: trunk/GSASII.py @ 1655

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

add a Show/Hide? command in the phase/data menu
add importer for ISIS SXD data

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