source: trunk/GSASII.py @ 1962

Last change on this file since 1962 was 1962, checked in by vondreele, 6 years ago

made PackageVersions? a list so it comes out in same order as the other list
slight reformatting so appearance is the same as well

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