source: trunk/GSASII.py @ 1625

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

add a parameter to result from G2stIO.GetPhaseData?
add modulation functions to G2Math
add modulation names to G2obj
implement various wave types for modulations
plot position modulation wave function on contour plots
implement shift of modulation plot by +/-/0 keys
temporarily default G2spc.GetSSfxuinel to 1,-1 site symm.
move maxSSwave dict out of parmDict - now in controlDict
implement import of Sawtooth parms from J2K files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 171.8 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2015-01-08 22:24:32 +0000 (Thu, 08 Jan 2015) $
6# $Author: vondreele $
7# $Revision: 1625 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1625 2015-01-08 22:24:32Z 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: 1625 $")
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'] = [{} for i in range(len(Bank['RefDict']['RefList']))]
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'] = [{} for i in range(len(rd.RefDict['RefList']))]
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.PatternId = 0
2093        self.PickId = 0
2094        self.PeakTable = []
2095        self.LimitsTable = []
2096        self.ifX20 = True   #use M20 /= (1+X20)
2097        self.HKL = []
2098        self.Lines = []
2099        self.itemPicked = None
2100        self.dataFrame = None
2101        self.Interpolate = 'nearest'
2102        self.ContourColor = 'Paired'
2103        self.VcovColor = 'RdYlGn'
2104        self.RamaColor = 'Blues'
2105        self.Projection = 'equal area'
2106        self.logPlot = False
2107        self.qPlot = False
2108        self.dPlot = False
2109        self.sqPlot = False
2110        self.SqrtPlot = False
2111        self.ErrorBars = False
2112        self.Contour = False
2113        self.Legend = False
2114        self.SinglePlot = True
2115        self.SubBack = False
2116        self.seqReverse = False
2117        self.plotView = 0
2118        self.Image = 0
2119        self.oldImagefile = ''
2120        self.ImageZ = []
2121        self.Integrate = 0
2122        self.imageDefault = {}
2123        self.Sngl = False
2124        self.ifGetRing = False
2125        self.MaskKey = ''           #trigger for making image masks
2126        self.StrainKey = ''         #ditto for new strain d-zeros
2127        self.EnablePlot = True
2128        self.Tutorials = False      #used for changing default directory
2129        arg = sys.argv
2130        if len(arg) > 1 and arg[1]:
2131            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2132            self.dirname = os.path.dirname(arg[1])
2133            if self.dirname: os.chdir(self.dirname)
2134            try:
2135                G2IO.ProjFileOpen(self)
2136                self.PatternTree.Expand(self.root)
2137                for item in self.Refine: item.Enable(True)
2138                self.EnableSeqRefineMenu()
2139            except:
2140                print 'Error opening file',arg[1]
2141
2142    def OnSize(self,event):
2143        'Called when the main window is resized. Not sure why'
2144        w,h = self.GetClientSizeTuple()
2145        self.mainPanel.SetSize(wx.Size(w,h))
2146        self.PatternTree.SetSize(wx.Size(w,h))
2147                       
2148    def OnPatternTreeSelChanged(self, event):
2149        '''Called when a data tree item is selected'''
2150        if self.TreeItemDelete:
2151            self.TreeItemDelete = False
2152        else:
2153            pltNum = self.G2plotNB.nb.GetSelection()
2154            if pltNum >= 0:                         #to avoid the startup with no plot!
2155                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2156                pltPlot = pltPage.figure
2157            item = event.GetItem()
2158            G2gd.MovePatternTreeToGrid(self,item)
2159            if self.oldFocus:
2160                self.oldFocus.SetFocus()
2161       
2162    def OnPatternTreeItemCollapsed(self, event):
2163        'Called when a tree item is collapsed'
2164        event.Skip()
2165
2166    def OnPatternTreeItemExpanded(self, event):
2167        'Called when a tree item is expanded'
2168        event.Skip()
2169       
2170    def OnPatternTreeItemDelete(self, event):
2171        'Called when a tree item is deleted -- not sure what this does'
2172        self.TreeItemDelete = True
2173
2174    def OnPatternTreeItemActivated(self, event):
2175        'Called when a tree item is activated'
2176        event.Skip()
2177       
2178    def OnPatternTreeBeginRDrag(self,event):
2179        # testing this - doesn't work! Binds commented out above
2180        event.Allow()
2181        self.BeginDragId = event.GetItem()
2182        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2183        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2184        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2185        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2186        while item:     #G2 data tree has no sub children under a child of a tree item
2187            name = self.PatternTree.GetItemText(item)
2188            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2189            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2190       
2191    def OnPatternTreeEndDrag(self,event):
2192        # testing this - doesn't work! Binds commented out above
2193        event.Allow()
2194        self.EndDragId = event.GetItem()
2195        try:
2196            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2197        except:
2198            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2199            NewParent = self.root
2200        if self.ParentId != NewParent:
2201            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2202        else:
2203            Name,Item = self.DragData[0]
2204            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2205            self.PatternTree.SetItemPyData(NewId,Item)
2206            for name,item in self.DragData[1:]:     #loop over children
2207                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2208                self.PatternTree.SetItemPyData(Id,item)
2209            self.PatternTree.Delete(self.BeginDragId)
2210            G2gd.MovePatternTreeToGrid(self,NewId)
2211       
2212    def OnPatternTreeKeyDown(self,event):
2213        'Allows stepping through the tree with the up/down arrow keys'
2214        key = event.GetKeyCode()
2215        item = self.PickId
2216        if type(item) is int: return # is this the toplevel in tree?
2217        if key == wx.WXK_UP:
2218            self.oldFocus = wx.Window.FindFocus()
2219            self.PatternTree.GetPrevSibling(item)
2220        elif key == wx.WXK_DOWN:
2221            self.oldFocus = wx.Window.FindFocus()
2222            self.PatternTree.GetNextSibling(item)
2223               
2224    def OnReadPowderPeaks(self,event):
2225        'Bound to menu Data/Read Powder Peaks -- still needed?'
2226        Cuka = 1.54052
2227        self.CheckNotebook()
2228        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
2229            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
2230        try:
2231            if dlg.ShowModal() == wx.ID_OK:
2232                self.HKL = []
2233                self.powderfile = dlg.GetPath()
2234                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
2235                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2236                data = ['PKS',Cuka,0.0]
2237                names = ['Type','Lam','Zero'] 
2238                codes = [0,0,0]
2239                inst = [G2IO.makeInstDict(names,data,codes),{}]
2240                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2241                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2242                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2243                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2244                self.PatternTree.Expand(Id)
2245                self.PatternTree.SelectItem(Id)
2246                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2247        finally:
2248            dlg.Destroy()
2249                       
2250    def OnImageRead(self,event):
2251        'Called to read in an image in any known format'
2252        self.CheckNotebook()
2253        dlg = wx.FileDialog(
2254            self, 'Choose image files', '.', '',
2255            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png)|'
2256            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png;*.zip|'
2257            'European detector file (*.edf)|*.edf|'
2258            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2259            'MAR file (*.mar*)|*.mar*|'
2260            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2261            'ADSC Image (*.img)|*.img|'
2262            'GSAS-II Image (*.G2img)|*.G2img|'
2263            'Portable Network Graphics image (*.png)|*.png|'
2264            'Zip archive (*.zip)|*.zip|'
2265            'All files (*.*)|*.*',
2266            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
2267        try:
2268            if dlg.ShowModal() == wx.ID_OK:
2269                imagefiles = dlg.GetPaths()
2270                imagefiles.sort()
2271                for imagefile in imagefiles:
2272                    # if a zip file, open and extract
2273                    if os.path.splitext(imagefile)[1].lower() == '.zip':
2274                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
2275                        if extractedfile is not None and extractedfile != imagefile:
2276                            imagefile = extractedfile
2277                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
2278                    if Comments:
2279                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
2280                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2281                        Imax = np.amax(Image)
2282                        Imin = max(0.0,np.amin(Image))          #force positive
2283                        if self.imageDefault:
2284                            Data = copy.copy(self.imageDefault)
2285                            Data['showLines'] = True
2286                            Data['ring'] = []
2287                            Data['rings'] = []
2288                            Data['cutoff'] = 10
2289                            Data['pixLimit'] = 20
2290                            Data['edgemin'] = 100000000
2291                            Data['calibdmin'] = 0.5
2292                            Data['calibskip'] = 0
2293                            Data['ellipses'] = []
2294                            Data['calibrant'] = ''
2295                            Data['GonioAngles'] = [0.,0.,0.]
2296                            Data['DetDepthRef'] = False
2297                        else:
2298                            Data['type'] = 'PWDR'
2299                            Data['color'] = 'Paired'
2300                            Data['tilt'] = 0.0
2301                            Data['rotation'] = 0.0
2302                            Data['showLines'] = False
2303                            Data['ring'] = []
2304                            Data['rings'] = []
2305                            Data['cutoff'] = 10
2306                            Data['pixLimit'] = 20
2307                            Data['calibdmin'] = 0.5
2308                            Data['calibskip'] = 0
2309                            Data['edgemin'] = 100000000
2310                            Data['ellipses'] = []
2311                            Data['GonioAngles'] = [0.,0.,0.]
2312                            Data['DetDepth'] = 0.
2313                            Data['DetDepthRef'] = False
2314                            Data['calibrant'] = ''
2315                            Data['IOtth'] = [2.0,5.0]
2316                            Data['LRazimuth'] = [135,225]
2317                            Data['azmthOff'] = 0.0
2318                            Data['outChannels'] = 2500
2319                            Data['outAzimuths'] = 1
2320                            Data['centerAzm'] = False
2321                            Data['fullIntegrate'] = False
2322                            Data['setRings'] = False
2323                            Data['background image'] = ['',-1.0]                           
2324                            Data['dark image'] = ['',-1.0]
2325                        Data['setDefault'] = False
2326                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2327                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
2328                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2329                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2330                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2331                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2332                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
2333                        self.PickId = Id
2334                        self.Image = Id
2335                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!               
2336                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
2337        finally:
2338            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2339            os.chdir(path)
2340            dlg.Destroy()
2341
2342    def CheckNotebook(self):
2343        '''Make sure the data tree has the minimally expected controls.
2344        (BHT) correct?
2345        '''
2346        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2347            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2348            self.PatternTree.SetItemPyData(sub,[''])
2349        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2350            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2351            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2352        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2353            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2354            self.PatternTree.SetItemPyData(sub,{})
2355        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2356            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2357            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2358        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2359            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2360            self.PatternTree.SetItemPyData(sub,{})
2361        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2362            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2363            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2364                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2365               
2366    class CopyDialog(wx.Dialog):
2367        '''Creates a dialog for copying control settings between
2368        data tree items'''
2369        def __init__(self,parent,title,text,data):
2370            wx.Dialog.__init__(self,parent,-1,title, 
2371                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2372            self.data = data
2373            panel = wx.Panel(self)
2374            mainSizer = wx.BoxSizer(wx.VERTICAL)
2375            topLabl = wx.StaticText(panel,-1,text)
2376            mainSizer.Add((10,10),1)
2377            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2378            mainSizer.Add((10,10),1)
2379            ncols = len(data)/40+1
2380            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2381            for id,item in enumerate(self.data):
2382                ckbox = wx.CheckBox(panel,id,item[1])
2383                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2384                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2385            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2386            OkBtn = wx.Button(panel,-1,"Ok")
2387            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2388            cancelBtn = wx.Button(panel,-1,"Cancel")
2389            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2390            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2391            btnSizer.Add((20,20),1)
2392            btnSizer.Add(OkBtn)
2393            btnSizer.Add((20,20),1)
2394            btnSizer.Add(cancelBtn)
2395            btnSizer.Add((20,20),1)
2396           
2397            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2398            panel.SetSizer(mainSizer)
2399            panel.Fit()
2400            self.Fit()
2401       
2402        def OnCopyChange(self,event):
2403            id = event.GetId()
2404            self.data[id][0] = self.FindWindowById(id).GetValue()       
2405           
2406        def OnOk(self,event):
2407            parent = self.GetParent()
2408            parent.Raise()
2409            self.EndModal(wx.ID_OK)             
2410           
2411        def OnCancel(self,event):
2412            parent = self.GetParent()
2413            parent.Raise()
2414            self.EndModal(wx.ID_CANCEL)             
2415           
2416        def GetData(self):
2417            return self.data
2418       
2419    class SumDialog(wx.Dialog):
2420        'Allows user to supply scale factor(s) when summing data'
2421        def __init__(self,parent,title,text,dataType,data):
2422            wx.Dialog.__init__(self,parent,-1,title, 
2423                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2424            self.data = data
2425            panel = wx.Panel(self)
2426            mainSizer = wx.BoxSizer(wx.VERTICAL)
2427            topLabl = wx.StaticText(panel,-1,text)
2428            mainSizer.Add((10,10),1)
2429            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2430            mainSizer.Add((10,10),1)
2431            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2432            for id,item in enumerate(self.data[:-1]):
2433                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2434                name.SetEditable(False)
2435                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2436                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2437                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2438                dataGridSizer.Add(scale,0,wx.LEFT,10)
2439                dataGridSizer.Add(name,0,wx.RIGHT,10)
2440            if dataType:
2441                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2442                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2443                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2444                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2445                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2446                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2447            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2448            OkBtn = wx.Button(panel,-1,"Ok")
2449            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2450            cancelBtn = wx.Button(panel,-1,"Cancel")
2451            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2452            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2453            btnSizer.Add((20,20),1)
2454            btnSizer.Add(OkBtn)
2455            btnSizer.Add((20,20),1)
2456            btnSizer.Add(cancelBtn)
2457            btnSizer.Add((20,20),1)
2458           
2459            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2460            panel.SetSizer(mainSizer)
2461            panel.Fit()
2462            self.Fit()
2463
2464        def OnScaleChange(self,event):
2465            id = event.GetId()
2466            value = self.FindWindowById(id).GetValue()
2467            try:
2468                self.data[id][0] = float(value)
2469                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2470            except ValueError:
2471                if value and '-' not in value[0]:
2472                    print 'bad input - numbers only'
2473                    self.FindWindowById(id).SetValue('0.000')
2474           
2475        def OnNameChange(self,event):
2476            self.data[-1] = self.name.GetValue() 
2477           
2478        def OnOk(self,event):
2479            parent = self.GetParent()
2480            parent.Raise()
2481            self.EndModal(wx.ID_OK)             
2482           
2483        def OnCancel(self,event):
2484            parent = self.GetParent()
2485            parent.Raise()
2486            self.EndModal(wx.ID_CANCEL)             
2487           
2488        def GetData(self):
2489            return self.data
2490                       
2491    def OnPwdrSum(self,event):
2492        'Sum together powder data(?)'
2493        TextList = []
2494        DataList = []
2495        SumList = []
2496        Names = []
2497        Inst = None
2498        SumItemList = []
2499        Comments = ['Sum equals: \n']
2500        if self.PatternTree.GetCount():
2501            item, cookie = self.PatternTree.GetFirstChild(self.root)
2502            while item:
2503                name = self.PatternTree.GetItemText(item)
2504                Names.append(name)
2505                if 'PWDR' in name:
2506                    TextList.append([0.0,name])
2507                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2508                    if not Inst:
2509                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2510                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2511            if len(TextList) < 2:
2512                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2513                return
2514            TextList.append('default_sum_name')               
2515            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2516            try:
2517                if dlg.ShowModal() == wx.ID_OK:
2518                    lenX = 0
2519                    Xminmax = [0,0]
2520                    Xsum = []
2521                    Ysum = []
2522                    Vsum = []
2523                    result = dlg.GetData()
2524                    for i,item in enumerate(result[:-1]):
2525                        scale,name = item
2526                        data = DataList[i]
2527                        if scale:
2528                            Comments.append("%10.3f %s" % (scale,' * '+name))
2529                            x,y,w,yc,yb,yd = data   #numpy arrays!
2530                            v = 1./w
2531                            if lenX:
2532                                if lenX != len(x):
2533                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2534                                        '\nExpected:'+str(lenX)+ \
2535                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2536                                    return
2537                            else:
2538                                lenX = len(x)
2539                            if Xminmax[1]:
2540                                if Xminmax != [x[0],x[-1]]:
2541                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2542                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2543                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2544                                    return
2545                                else:
2546                                    for j,yi in enumerate(y):
2547                                         Ysum[j] += scale*yi
2548                                         Vsum[j] += abs(scale)*v[j]
2549                            else:
2550                                Xminmax = [x[0],x[-1]]
2551                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2552                                for j,yi in enumerate(y):
2553                                    Xsum.append(x[j])
2554                                    Ysum.append(scale*yi)
2555                                    Vsum.append(abs(scale*v[j]))
2556                    Wsum = 1./np.array(Vsum)
2557                    outname = 'PWDR '+result[-1]
2558                    Id = 0
2559                    if outname in Names:
2560                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2561                        try:
2562                            if dlg2.ShowModal() == wx.ID_OK:
2563                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2564                                self.PatternTree.Delete(Id)
2565                        finally:
2566                            dlg2.Destroy()
2567                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2568                    if Id:
2569                        Sample = G2pdG.SetDefaultSample()
2570                        valuesdict = {
2571                            'wtFactor':1.0,
2572                            'Dummy':False,
2573                            'ranId':ran.randint(0,sys.maxint),
2574                            }
2575                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2576                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2577                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2578                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2579                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2580                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2581                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2582                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2583                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2584                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2585                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2586                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2587                        self.PatternTree.SelectItem(Id)
2588                        self.PatternTree.Expand(Id)
2589            finally:
2590                dlg.Destroy()
2591
2592    def OnImageSum(self,event):
2593        'Sum together image data(?)'
2594        TextList = []
2595        DataList = []
2596        SumList = []
2597        Names = []
2598        Inst = []
2599        SumItemList = []
2600        Comments = ['Sum equals: \n']
2601        if self.PatternTree.GetCount():
2602            item, cookie = self.PatternTree.GetFirstChild(self.root)
2603            while item:
2604                name = self.PatternTree.GetItemText(item)
2605                Names.append(name)
2606                if 'IMG' in name:
2607                    TextList.append([0.0,name])
2608                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2609                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2610                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2611            if len(TextList) < 2:
2612                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2613                return
2614            TextList.append('default_sum_name')               
2615            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2616            try:
2617                if dlg.ShowModal() == wx.ID_OK:
2618                    imSize = 0
2619                    result = dlg.GetData()
2620                    First = True
2621                    Found = False
2622                    for i,item in enumerate(result[:-1]):
2623                        scale,name = item
2624                        data = DataList[i]
2625                        if scale:
2626                            Found = True                               
2627                            Comments.append("%10.3f %s" % (scale,' * '+name))
2628                            Npix,imagefile = data
2629                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2630                            if First:
2631                                newImage = np.zeros_like(image)
2632                                First = False
2633                            if imSize:
2634                                if imSize != Npix:
2635                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2636                                        '\nExpected:'+str(imSize)+ \
2637                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2638                                    return
2639                                newImage = newImage+scale*image
2640                            else:
2641                                imSize = Npix
2642                                newImage = newImage+scale*image
2643                            del(image)
2644                    if not Found:
2645                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2646                        return
2647                       
2648                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2649                    outname = 'IMG '+result[-1]
2650                    Id = 0
2651                    if outname in Names:
2652                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2653                        try:
2654                            if dlg2.ShowModal() == wx.ID_OK:
2655                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2656                        finally:
2657                            dlg2.Destroy()
2658                    else:
2659                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2660                    if Id:
2661                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2662                            'G2img files (*.G2img)|*.G2img', 
2663                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2664                        if dlg.ShowModal() == wx.ID_OK:
2665                            newimagefile = dlg.GetPath()
2666                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2667                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2668                            Imax = np.amax(newImage)
2669                            Imin = np.amin(newImage)
2670                            newImage = []
2671                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2672                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2673                        del(newImage)
2674                        if self.imageDefault:
2675                            Data = copy.copy(self.imageDefault)
2676                        Data['showLines'] = True
2677                        Data['ring'] = []
2678                        Data['rings'] = []
2679                        Data['cutoff'] = 10
2680                        Data['pixLimit'] = 20
2681                        Data['ellipses'] = []
2682                        Data['calibrant'] = ''
2683                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2684                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2685                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2686                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2687                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2688                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2689                        self.PatternTree.SelectItem(Id)
2690                        self.PatternTree.Expand(Id)
2691                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2692                        self.Image = self.PickId
2693            finally:
2694                dlg.Destroy()
2695                     
2696    def OnAddPhase(self,event):
2697        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2698        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2699            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2700        else:
2701            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2702        PhaseName = ''
2703        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2704            style=wx.OK)
2705        if dlg.ShowModal() == wx.ID_OK:
2706            PhaseName = dlg.GetValue()
2707        dlg.Destroy()
2708        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2709        E,SGData = G2spc.SpcGroup('P 1')
2710        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2711       
2712    def OnDeletePhase(self,event):
2713        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2714        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2715        if self.dataFrame:
2716            self.dataFrame.Clear() 
2717        TextList = []
2718        DelList = []
2719        DelItemList = []
2720        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2721            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2722        else:
2723            return
2724        if sub:
2725            item, cookie = self.PatternTree.GetFirstChild(sub)
2726            while item:
2727                TextList.append(self.PatternTree.GetItemText(item))
2728                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2729            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2730            try:
2731                if dlg.ShowModal() == wx.ID_OK:
2732                    result = dlg.GetSelections()
2733                    for i in result: DelList.append([i,TextList[i]])
2734                    item, cookie = self.PatternTree.GetFirstChild(sub)
2735                    i = 0
2736                    while item:
2737                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2738                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2739                        i += 1
2740                    for item in DelItemList:
2741                        name = self.PatternTree.GetItemText(item)
2742                        self.PatternTree.Delete(item)
2743                        self.G2plotNB.Delete(name)
2744                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2745                    while item:
2746                        name = self.PatternTree.GetItemText(item)
2747                        if 'PWDR' in name:
2748                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2749                            refList = self.PatternTree.GetItemPyData(Id)
2750                            if len(refList):
2751                                for i,item in DelList:
2752                                    if item in refList:
2753                                        del(refList[item])
2754                            self.PatternTree.SetItemPyData(Id,refList)
2755                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2756            finally:
2757                dlg.Destroy()
2758               
2759    def OnRenameData(self,event):
2760        'Renames an existing phase. Called by Data/Rename Phase menu'
2761        name = self.PatternTree.GetItemText(self.PickId)     
2762        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2763            dataType = name[:name.index(' ')+1]                 #includes the ' '
2764            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2765                defaultValue=name[name.index(' ')+1:])
2766            try:
2767                if dlg.ShowModal() == wx.ID_OK:
2768                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2769            finally:
2770                dlg.Destroy()
2771       
2772    def GetFileList(self,fileType,skip=None):        #potentially useful?
2773        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2774        fileList = []
2775        Source = ''
2776        id, cookie = self.PatternTree.GetFirstChild(self.root)
2777        while id:
2778            name = self.PatternTree.GetItemText(id)
2779            if fileType in name:
2780                if id == skip:
2781                    Source = name
2782                else:
2783                    fileList.append([False,name,id])
2784            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2785        if skip:
2786            return fileList,Source
2787        else:
2788            return fileList
2789           
2790    def OnDataDelete(self, event):
2791        '''Delete one or more histograms from data tree. Called by the
2792        Data/DeleteData menu
2793        '''
2794        TextList = ['All Data']
2795        DelList = []
2796        DelItemList = []
2797        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
2798        ifPWDR = False
2799        ifSASD = False
2800        ifIMG = False
2801        ifHKLF = False
2802        ifPDF = False
2803        if self.PatternTree.GetCount():
2804            item, cookie = self.PatternTree.GetFirstChild(self.root)
2805            while item:
2806                name = self.PatternTree.GetItemText(item)
2807                if name not in ['Notebook','Controls','Covariance','Constraints',
2808                    'Restraints','Phases','Rigid bodies']:
2809                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
2810                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
2811                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
2812                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
2813                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
2814                    TextList.append(name)
2815                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2816            if ifPWDR: TextList.insert(1,'All PWDR')
2817            if ifSASD: TextList.insert(1,'All SASD')
2818            if ifIMG: TextList.insert(1,'All IMG')
2819            if ifHKLF: TextList.insert(1,'All HKLF')
2820            if ifPDF: TextList.insert(1,'All PDF')               
2821            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2822            try:
2823                if dlg.ShowModal() == wx.ID_OK:
2824                    result = dlg.GetSelections()
2825                    for i in result: DelList.append(TextList[i])
2826                    if 'All Data' in DelList:
2827                        DelList = [item for item in TextList if item[:3] != 'All']
2828                    elif 'All PWDR' in DelList:
2829                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2830                    elif 'All SASD' in DelList:
2831                        DelList = [item for item in TextList if item[:4] == 'SASD']
2832                    elif 'All IMG' in DelList:
2833                        DelList = [item for item in TextList if item[:3] == 'IMG']
2834                    elif 'All HKLF' in DelList:
2835                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2836                    elif 'All PDF' in DelList:
2837                        DelList = [item for item in TextList if item[:3] == 'PDF']
2838                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2839                    while item:
2840                        itemName = self.PatternTree.GetItemText(item)
2841                        if itemName in DelList:
2842                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
2843                            elif 'SASD' in itemName: nItems['SASD'] -= 1
2844                            elif 'IMG' in itemName: nItems['IMG'] -= 1
2845                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
2846                            elif 'PDF' in itemName: nItems['PDF'] -= 1
2847                            DelItemList.append(item)
2848                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2849                    for item in DelItemList:
2850                        self.PatternTree.Delete(item)
2851                    self.PickId = 0
2852                    self.PatternId = 0
2853                    if nItems['PWDR']:
2854                        wx.CallAfter(G2plt.PlotPatterns,self,True)
2855                    else:
2856                        self.G2plotNB.Delete('Powder Patterns')
2857                    if not nItems['IMG']:
2858                        self.G2plotNB.Delete('2D Powder Image')
2859                    if not nItems['HKLF']:
2860                        self.G2plotNB.Delete('Structure Factors')
2861                        if '3D Structure Factors' in self.G2plotNB.plotList:
2862                            self.G2plotNB.Delete('3D Structure Factors')
2863            finally:
2864                dlg.Destroy()
2865
2866    def OnFileOpen(self, event, filename=None):
2867        '''Reads in a GSAS-II .gpx project file in response to the
2868        File/Open Project menu button
2869        '''
2870        result = wx.ID_OK
2871        Id = 0
2872        self.EnablePlot = False
2873        if self.PatternTree.GetChildrenCount(self.root,False):
2874            if self.dataFrame:
2875                self.dataFrame.Clear() 
2876            dlg = wx.MessageDialog(
2877                self,
2878                'Do you want to overwrite the current project? '
2879                'Any unsaved changes will be lost. Press OK to continue.',
2880                'Overwrite?',  wx.OK | wx.CANCEL)
2881            try:
2882                result = dlg.ShowModal()
2883                if result == wx.ID_OK:
2884                    self.PatternTree.DeleteChildren(self.root)
2885                    self.GSASprojectfile = ''
2886                    if self.HKL: self.HKL = []
2887                    if self.G2plotNB.plotList:
2888                        self.G2plotNB.clear()
2889            finally:
2890                dlg.Destroy()
2891        if result != wx.ID_OK: return
2892
2893        if not filename:
2894            if self.dataDisplay: self.dataDisplay.Destroy()
2895            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2896                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2897            try:
2898                if dlg.ShowModal() != wx.ID_OK: return
2899                self.GSASprojectfile = dlg.GetPath()
2900                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2901                self.dirname = dlg.GetDirectory()
2902            finally:
2903                dlg.Destroy()
2904        else:
2905            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2906            self.dirname = os.path.split(filename)[0]
2907
2908        G2IO.ProjFileOpen(self)
2909        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2910        self.PatternTree.Expand(self.root)
2911        self.HKL = []
2912        item, cookie = self.PatternTree.GetFirstChild(self.root)
2913        while item and not Id:
2914            name = self.PatternTree.GetItemText(item)
2915            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
2916                Id = item
2917            elif name == 'Controls':
2918                data = self.PatternTree.GetItemPyData(item)
2919                if data:
2920                    for item in self.Refine: item.Enable(True)
2921                    self.EnableSeqRefineMenu()
2922            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2923        if Id:
2924            self.EnablePlot = True
2925            self.PatternTree.SelectItem(Id)
2926        self.CheckNotebook()
2927        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2928
2929    def OnFileClose(self, event):
2930        '''Clears the data tree in response to the
2931        File/New Project menu button. User is given option to save
2932        the project.
2933        '''
2934        if self.dataFrame:
2935            self.dataFrame.Clear()
2936            self.dataFrame.SetLabel('GSAS-II data display') 
2937        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2938        try:
2939            result = dlg.ShowModal()
2940            if result == wx.ID_OK:
2941                self.OnFileSaveMenu(event)
2942            if result != wx.ID_CANCEL:
2943                self.GSASprojectfile = ''
2944                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2945                self.PatternTree.DeleteChildren(self.root)
2946                if self.HKL: self.HKL = []
2947                if self.G2plotNB.plotList:
2948                    self.G2plotNB.clear()
2949        finally:
2950            dlg.Destroy()
2951
2952    def OnFileSave(self, event):
2953        '''Save the current project in response to the
2954        File/Save Project menu button
2955        '''
2956       
2957        if self.GSASprojectfile: 
2958            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2959            G2IO.ProjFileSave(self)
2960        else:
2961            self.OnFileSaveas(event)
2962
2963    def OnFileSaveas(self, event):
2964        '''Save the current project in response to the
2965        File/Save as menu button
2966        '''
2967        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2968            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2969        try:
2970            if dlg.ShowModal() == wx.ID_OK:
2971                self.GSASprojectfile = dlg.GetPath()
2972                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2973                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2974                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2975                G2IO.ProjFileSave(self)
2976                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2977        finally:
2978            dlg.Destroy()
2979
2980    def ExitMain(self, event):
2981        '''Called if the main window is closed'''
2982        if self.undofile:
2983            os.remove(self.undofile)
2984        sys.exit()
2985       
2986    def OnFileExit(self, event):
2987        '''Called in response to the File/Quit menu button'''
2988        if self.dataFrame:
2989            self.dataFrame.Clear() 
2990            self.dataFrame.Destroy()
2991        self.Close()
2992       
2993    # def OnExportPatterns(self,event):
2994    #     names = ['All']
2995    #     exports = []
2996    #     item, cookie = self.PatternTree.GetFirstChild(self.root)
2997    #     while item:
2998    #         name = self.PatternTree.GetItemText(item)
2999    #         if 'PWDR' in name:
3000    #             names.append(name)
3001    #         item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3002    #     if names:
3003    #         dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
3004    #         if dlg.ShowModal() == wx.ID_OK:
3005    #             sel = dlg.GetSelections()
3006    #             if sel[0] == 0:
3007    #                 exports = names[1:]
3008    #             else:
3009    #                 for x in sel:
3010    #                     exports.append(names[x])
3011    #         dlg.Destroy()
3012    #     if exports:
3013    #         dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '',
3014    #             'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
3015    #             wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3016    #         try:
3017    #             if dlg.ShowModal() == wx.ID_OK:
3018    #                 powderfile = dlg.GetPath()
3019    #                 powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
3020    #                 if 'fxye' in powderfile:
3021    #                     G2IO.powderFxyeSave(self,exports,powderfile)
3022    #                 else:       #just xye
3023    #                     G2IO.powderXyeSave(self,exports,powderfile)
3024    #         finally:
3025    #             dlg.Destroy()
3026       
3027    def OnExportPeakList(self,event):
3028        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
3029            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3030        try:
3031            if dlg.ShowModal() == wx.ID_OK:
3032                self.peaklistfile = dlg.GetPath()
3033                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3034                file = open(self.peaklistfile,'w')               
3035                item, cookie = self.PatternTree.GetFirstChild(self.root)
3036                while item:
3037                    name = self.PatternTree.GetItemText(item)
3038                    if 'PWDR' in name:
3039                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3040                        while item2:
3041                            name2 = self.PatternTree.GetItemText(item2)
3042                            if name2 == 'Peak List':
3043                                peaks = self.PatternTree.GetItemPyData(item2)['peaks']
3044                                file.write("%s \n" % (name+' Peak List'))
3045                                if len(peaks[0]) == 8:
3046                                    file.write('%10s %12s %10s %10s\n'%('pos','int','sig','gam'))
3047                                else:
3048                                    file.write('%10s %12s %10s %10s %10s %10s\n'%('pos','int','alp','bet','sig','gam'))                                   
3049                                for peak in peaks:
3050                                    if len(peak) == 8:  #CW
3051                                        file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
3052                                            (peak[0],peak[2],peak[4],peak[6]))
3053                                    else:               #TOF - more cols
3054                                        file.write("%10.5f %12.2f %10.3f %10.3f %10.3f %10.3f\n" % \
3055                                            (peak[0],peak[2],peak[4],peak[6],peak[8],peak[10]))
3056                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3057                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3058                file.close()
3059        finally:
3060            dlg.Destroy()
3061       
3062    def OnExportHKL(self,event):
3063        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
3064            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3065        try:
3066            if dlg.ShowModal() == wx.ID_OK:
3067                self.peaklistfile = dlg.GetPath()
3068                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3069                file = open(self.peaklistfile,'w')               
3070                item, cookie = self.PatternTree.GetFirstChild(self.root)
3071                while item:
3072                    name = self.PatternTree.GetItemText(item)
3073                    if 'PWDR' in name:
3074                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3075                        while item2:
3076                            name2 = self.PatternTree.GetItemText(item2)
3077                            if name2 == 'Reflection Lists':
3078                                data = self.PatternTree.GetItemPyData(item2)
3079                                phases = data.keys()
3080                                for phase in phases:
3081                                    peaks = data[phase]
3082                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3083                                    if 'T' in peaks.get('Type','PXC'):
3084                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3085                                    else:               
3086                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3087                                    for peak in peaks['RefList']:
3088                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
3089                                        if 'T' in peaks.get('Type','PXC'):
3090                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3091                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3092                                        else:
3093                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3094                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3095                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3096                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3097                file.close()
3098        finally:
3099            dlg.Destroy()
3100       
3101    def OnExportPDF(self,event):
3102        #need S(Q) and G(R) to be saved here - probably best from selection?
3103        names = ['All']
3104        exports = []
3105        item, cookie = self.PatternTree.GetFirstChild(self.root)
3106        while item:
3107            name = self.PatternTree.GetItemText(item)
3108            if 'PDF' in name:
3109                names.append(name)
3110            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3111        if names:
3112            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3113            if dlg.ShowModal() == wx.ID_OK:
3114                sel = dlg.GetSelections()
3115                if sel[0] == 0:
3116                    exports = names[1:]
3117                else:
3118                    for x in sel:
3119                        exports.append(names[x])
3120            dlg.Destroy()
3121        if exports:
3122            G2IO.PDFSave(self,exports)
3123       
3124    def OnMakePDFs(self,event):
3125        '''Calculates PDFs
3126        '''
3127        sind = lambda x: math.sin(x*math.pi/180.)
3128        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3129        TextList = ['All PWDR']
3130        PDFlist = []
3131        Names = []
3132        if self.PatternTree.GetCount():
3133            id, cookie = self.PatternTree.GetFirstChild(self.root)
3134            while id:
3135                name = self.PatternTree.GetItemText(id)
3136                Names.append(name)
3137                if 'PWDR' in name:
3138                    TextList.append(name)
3139                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3140            if len(TextList) == 1:
3141                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3142                return
3143            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3144            try:
3145                if dlg.ShowModal() == wx.ID_OK:
3146                    result = dlg.GetSelections()
3147                    for i in result: PDFlist.append(TextList[i])
3148                    if 0 in result:
3149                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3150                    for item in PDFlist:
3151                        PWDRname = item[4:]
3152                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3153                        Data = {
3154                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3155                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3156                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3157                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3158                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3159                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3160                            'Lorch':True,}
3161                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3162                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3163                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3164                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3165                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3166                for item in self.ExportPDF: item.Enable(True)
3167            finally:
3168                dlg.Destroy()
3169               
3170    def GetPWDRdatafromTree(self,PWDRname):
3171        ''' Returns powder data from GSASII tree
3172
3173        :param str PWDRname: a powder histogram name as obtained from
3174          :meth:`GSASIIstruct.GetHistogramNames`
3175
3176        :returns: PWDRdata = powder data dictionary with
3177          Powder data arrays, Limits, Instrument Parameters,
3178          Sample Parameters           
3179        '''
3180        PWDRdata = {}
3181        try:
3182            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3183        except ValueError:
3184            PWDRdata['wtFactor'] = 1.0
3185        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3186        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3187        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3188        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3189        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3190        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3191        if 'ranId' not in PWDRdata:  # patch, add a random Id
3192            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3193        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3194            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3195        return PWDRdata
3196
3197    def GetHKLFdatafromTree(self,HKLFname):
3198        ''' Returns single crystal data from GSASII tree
3199
3200        :param str HKLFname: a single crystal histogram name as obtained
3201          from
3202          :meth:`GSASIIstruct.GetHistogramNames`
3203
3204        :returns: HKLFdata = single crystal data list of reflections
3205
3206        '''
3207        HKLFdata = {}
3208        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3209#        try:
3210#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3211#        except ValueError:
3212#            HKLFdata['wtFactor'] = 1.0
3213        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3214        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3215        return HKLFdata
3216       
3217    def GetPhaseData(self):
3218        '''Returns a dict with defined phases.
3219        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3220        get same info from GPX file.
3221        '''
3222        phaseData = {}
3223        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3224            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3225        else:
3226            print 'no phases found in GetPhaseData'
3227            sub = None
3228        if sub:
3229            item, cookie = self.PatternTree.GetFirstChild(sub)
3230            while item:
3231                phaseName = self.PatternTree.GetItemText(item)
3232                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3233                if 'ranId' not in phaseData[phaseName]:
3234                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3235                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3236        return phaseData
3237
3238    def GetPhaseInfofromTree(self):
3239        '''Get the phase names and their rId values,
3240        also the histograms used in each phase.
3241
3242        :returns: (phaseRIdList, usedHistograms) where
3243
3244          * phaseRIdList is a list of random Id values for each phase
3245          * usedHistograms is a dict where the keys are the phase names
3246            and the values for each key are a list of the histogram names
3247            used in each phase.
3248        '''
3249        phaseRIdList = []
3250        usedHistograms = {}
3251        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3252        if sub:
3253            item, cookie = self.PatternTree.GetFirstChild(sub)
3254            while item:
3255                phaseName = self.PatternTree.GetItemText(item)
3256                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3257                if ranId: phaseRIdList.append(ranId)
3258                data = self.PatternTree.GetItemPyData(item)
3259                UseList = data['Histograms']
3260                usedHistograms[phaseName] = UseList.keys()
3261                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3262        return phaseRIdList,usedHistograms
3263
3264    def GetPhaseNames(self):
3265        '''Returns a list of defined phases.
3266        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3267        get same info from GPX file.
3268        '''
3269        phaseNames = []
3270        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3271            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3272        else:
3273            print 'no phases found in GetPhaseNames'
3274            sub = None
3275        if sub:
3276            item, cookie = self.PatternTree.GetFirstChild(sub)
3277            while item:
3278                phase = self.PatternTree.GetItemText(item)
3279                phaseNames.append(phase)
3280                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3281        return phaseNames
3282   
3283    def GetHistogramNames(self,hType):
3284        """ Returns a list of histogram names found in the GSASII data tree
3285        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3286        get same info from GPX file.
3287       
3288        :param str hType: list of histogram types
3289        :return: list of histogram names
3290       
3291        """
3292        HistogramNames = []
3293        if self.PatternTree.GetCount():
3294            item, cookie = self.PatternTree.GetFirstChild(self.root)
3295            while item:
3296                name = self.PatternTree.GetItemText(item)
3297                if name[:4] in hType:
3298                    HistogramNames.append(name)       
3299                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3300
3301        return HistogramNames
3302                   
3303    def GetUsedHistogramsAndPhasesfromTree(self):
3304        ''' Returns all histograms that are found in any phase
3305        and any phase that uses a histogram.
3306        This also assigns numbers to used phases and histograms by the
3307        order they appear in the file.
3308        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3309        get same info from GPX file.
3310
3311        :returns: (Histograms,Phases)
3312
3313            * Histograms = dictionary of histograms as {name:data,...}
3314            * Phases = dictionary of phases that use histograms
3315        '''
3316        Histograms = {}
3317        Phases = {}
3318        phaseNames = self.GetPhaseNames()
3319        phaseData = self.GetPhaseData()
3320        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3321
3322        for phase in phaseData:
3323            Phase = phaseData[phase]
3324            pId = phaseNames.index(phase)
3325            Phase['pId'] = pId
3326            if Phase['Histograms']:
3327                if phase not in Phases:
3328                    Phases[phase] = Phase
3329                for hist in Phase['Histograms']:
3330                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3331                        Phase['Histograms'][hist]['Use'] = True         
3332                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3333                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3334                        if item:
3335                            if 'PWDR' in hist[:4]: 
3336                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3337                            elif 'HKLF' in hist[:4]:
3338                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3339                            hId = histoList.index(hist)
3340                            Histograms[hist]['hId'] = hId
3341                        else: # would happen if a referenced histogram were renamed or deleted
3342                            print('For phase "'+str(phase)+
3343                                  '" unresolved reference to histogram "'+str(hist)+'"')
3344        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3345        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3346        return Histograms,Phases
3347       
3348    def MakeLSParmDict(self):
3349        '''Load all parameters used for computation from the tree into a
3350        dict of paired values [value, refine flag]. Note that this is
3351        different than the parmDict used in the refinement, which only has
3352        values.
3353
3354        Note that similar things are done in
3355        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3356        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3357        a GPX file).
3358
3359        :returns: (parmDict,varyList) where:
3360
3361         * parmDict is a dict with values and refinement flags
3362           for each parameter and
3363         * varyList is a list of variables (refined parameters).
3364        '''
3365        parmDict = {}
3366        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3367        for phase in Phases:
3368            if 'pId' not in Phases[phase]:
3369                self.ErrorDialog('View parameter error','You must run least squares at least once')
3370                raise Exception,'No pId for phase '+str(phase)
3371        rigidbodyDict = self.PatternTree.GetItemPyData(   
3372            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3373        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3374        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3375        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3376        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3377        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3378        varyList = rbVary+phaseVary+hapVary+histVary
3379        parmDict.update(rbDict)
3380        parmDict.update(phaseDict)
3381        parmDict.update(hapDict)
3382        parmDict.update(histDict)
3383        for parm in parmDict:
3384            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3385                'Omega','Chi','Phi','nDebye','nPeaks']:
3386                parmDict[parm] = [parmDict[parm],'-']
3387            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3388                parmDict[parm] = [parmDict[parm],'-']
3389            elif parm in varyList:
3390                parmDict[parm] = [parmDict[parm],'T']
3391            else:
3392                parmDict[parm] = [parmDict[parm],'F']
3393        # for i in parmDict: print i,'\t',parmDict[i]
3394        # fl = open('parmDict.dat','wb')
3395        # import cPickle
3396        # cPickle.dump(parmDict,fl,1)
3397        # fl.close()
3398        return parmDict,varyList
3399
3400    def ShowLSParms(self,event):
3401        '''Displays a window showing all parameters in the refinement.
3402        Called from the Calculate/View LS Parms menu.
3403        '''
3404        parmDict,varyList = self.MakeLSParmDict()
3405        parmValDict = {}
3406        for i in parmDict:
3407            parmValDict[i] = parmDict[i][0]
3408           
3409        reqVaryList = tuple(varyList) # save requested variables
3410        try:
3411            # process constraints
3412            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3413            Constraints = self.PatternTree.GetItemPyData(sub)
3414            constList = []
3415            for item in Constraints:
3416                if item.startswith('_'): continue
3417                constList += Constraints[item]
3418            G2mv.InitVars()
3419            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3420            groups,parmlist = G2mv.GroupConstraints(constrDict)
3421            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3422            G2mv.Map2Dict(parmValDict,varyList)
3423        except:
3424            pass
3425        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3426        dlg.ShowModal()
3427        dlg.Destroy()
3428       
3429    def OnRefine(self,event):
3430        '''Perform a refinement.
3431        Called from the Calculate/Refine menu.
3432        '''       
3433        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3434        if Id:
3435            dlg = wx.MessageDialog(
3436                self,
3437                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3438                'Remove sequential results?',wx.OK|wx.CANCEL)
3439            if dlg.ShowModal() == wx.ID_OK:
3440                self.PatternTree.Delete(Id)
3441                dlg.Destroy()
3442            else:
3443                dlg.Destroy()
3444                return
3445
3446        self.OnFileSave(event)
3447        # check that constraints are OK here
3448        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3449        if errmsg:
3450            print('Error in constraints:\n'+errmsg+
3451                  '\nRefinement not possible')
3452            self.ErrorDialog('Constraint Error',
3453                             'Error in constraints:\n'+errmsg+
3454                             '\nRefinement not possible')
3455            return
3456        if warnmsg:
3457            print('Conflict between refinment flag settings and constraints:\n'+
3458                  warnmsg+'\nRefinement not possible')
3459            self.ErrorDialog('Refinement Flag Error',
3460                             'Conflict between refinment flag settings and constraints:\n'+
3461                             warnmsg+
3462                             '\nRefinement not possible')
3463            return
3464        #works - but it'd be better if it could restore plots
3465        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3466            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3467            parent=self)
3468        Size = dlg.GetSize()
3469        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3470            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3471        dlg.CenterOnParent()
3472        Rw = 100.00
3473        try:
3474            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3475        finally:
3476            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3477            dlg.Destroy()
3478            wx.Yield()
3479        oldId =  self.PatternTree.GetSelection()        #retain current selection
3480        oldName = self.PatternTree.GetItemText(oldId)
3481        parentId = self.PatternTree.GetItemParent(oldId)
3482        parentName = ''
3483        if parentId:
3484            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3485        dlg2 = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3486        try:
3487            if dlg2.ShowModal() == wx.ID_OK:
3488                Id = 0
3489                self.PatternTree.DeleteChildren(self.root)
3490                if self.HKL: self.HKL = []
3491                if self.G2plotNB.plotList:
3492                    self.G2plotNB.clear()
3493                G2IO.ProjFileOpen(self)
3494                item, cookie = self.PatternTree.GetFirstChild(self.root)
3495                while item and not Id:
3496                    name = self.PatternTree.GetItemText(item)
3497                    if name[:4] in ['PWDR','HKLF']:
3498                        Id = item
3499                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3500                if Id:
3501                    self.PatternTree.SelectItem(Id)
3502                if parentName:
3503                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3504                    if parentId:
3505                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3506                    else:
3507                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3508                    self.PatternTree.SelectItem(itemId)
3509        finally:
3510            dlg2.Destroy()
3511
3512    def OnSeqRefine(self,event):
3513        '''Perform a sequential refinement.
3514        Called from the Calculate/Sequential refine menu.
3515        '''       
3516        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3517        if not Id:
3518            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3519            self.PatternTree.SetItemPyData(Id,{})           
3520        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3521        Controls['ShowCell'] = True
3522        self.OnFileSave(event)
3523        # check that constraints are OK here
3524        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3525        if errmsg:
3526            print('Error in constraints:\n'+errmsg+
3527                  '\nRefinement not possible')
3528            self.ErrorDialog('Constraint Error',
3529                             'Error in constraints:\n'+errmsg+
3530                             '\nRefinement not possible')
3531            return
3532        if warnmsg:
3533            print('Conflict between refinment flag settings and constraints:\n'+
3534                  warnmsg+'\nRefinement not possible')
3535            self.ErrorDialog('Refinement Flag Error',
3536                             'Conflict between refinment flag settings and constraints:\n'+
3537                             warnmsg+'\nRefinement not possible')
3538            return
3539        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3540            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3541            parent=self)           
3542        Size = dlg.GetSize()
3543        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3544            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3545        dlg.CenterOnParent()
3546        try:
3547            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3548        finally:
3549            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3550            dlg.Destroy()
3551            wx.Yield()
3552        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3553        try:
3554            if dlg.ShowModal() == wx.ID_OK:
3555                Id = 0
3556                self.PatternTree.DeleteChildren(self.root)
3557                if self.HKL: self.HKL = []
3558                if self.G2plotNB.plotList:
3559                    self.G2plotNB.clear()
3560                G2IO.ProjFileOpen(self)
3561                Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3562                self.PatternTree.SelectItem(Id)
3563
3564        finally:
3565            dlg.Destroy()
3566       
3567    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3568        'Display an error message'
3569        result = None
3570        if parent is None:
3571            dlg = wx.MessageDialog(self, message, title,  wtype)
3572        else:
3573            dlg = wx.MessageDialog(parent, message, title,  wtype)
3574            dlg.CenterOnParent() # not working on Mac
3575        try:
3576            result = dlg.ShowModal()
3577        finally:
3578            dlg.Destroy()
3579        return result
3580
3581class GSASIImain(wx.App):
3582    '''Defines a wxApp for GSAS-II
3583
3584    Creates a wx frame (self.main) which contains the display of the
3585    data tree.
3586    '''
3587    def OnInit(self):
3588        '''Called automatically when the app is created.'''
3589        if '2.7' not in sys.version[:5]:
3590            dlg = wx.MessageDialog(None, 
3591                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version[:5],
3592                'Python version error',  wx.OK)
3593            try:
3594                result = dlg.ShowModal()
3595            finally:
3596                dlg.Destroy()
3597            sys.exit()
3598        self.main = GSASII(None)
3599        self.main.Show()
3600        self.SetTopWindow(self.main)
3601        # DEBUG: jump to sequential results
3602        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3603        #self.main.PatternTree.SelectItem(Id)
3604        # end DEBUG
3605        return True
3606    # def MacOpenFile(self, filename):
3607    #     '''Called on Mac every time a file is dropped on the app when it is running,
3608    #     treat this like a File/Open project menu action.
3609    #     Should be ignored on other platforms
3610    #     '''
3611    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3612    #     print 'MacOpen',filename
3613    #     if os.path.splitext(filename)[1] == '.py': return
3614    #     # end PATCH
3615    #     self.main.OnFileOpen(None,filename)
3616    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3617   
3618def main():
3619    '''Start up the GSAS-II application'''
3620    #application = GSASIImain() # don't redirect output, someday we
3621    # may want to do this if we can
3622    application = GSASIImain(0)
3623    if wxInspector:
3624        import wx.lib.inspection as wxeye
3625        wxeye.InspectionTool().Show()
3626
3627    #application.main.OnRefine(None)
3628    application.MainLoop()
3629   
3630if __name__ == '__main__':
3631    # print versions
3632    print "Python module versions loaded:"
3633    print "python:     ",sys.version[:5]
3634    print "wxpython:   ",wx.__version__
3635    print "matplotlib: ",mpl.__version__
3636    print "numpy:      ",np.__version__
3637    print "scipy:      ",sp.__version__
3638    print "OpenGL:     ",ogl.__version__
3639    try:
3640        from PIL import Image
3641        try:
3642            from PIL import PILLOW_VERSION
3643            version = PILLOW_VERSION
3644        except:
3645            version = Image.VERSION
3646        print "pillow:     ",version
3647    except ImportError:
3648        try:
3649            import Image
3650            print "Image (PIL):",Image.VERSION
3651        except ImportError:
3652            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3653    try:
3654        import mkl
3655        print "Max threads ",mkl.get_max_threads()
3656    except:
3657        pass
3658    import platform
3659    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3660    #print "wxPython description",wx.PlatformInfo
3661    print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
3662    GSASIIpath.InvokeDebugOpts()
3663    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.