source: trunk/GSASII.py @ 1956

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

fix (again) fill unit cell, dynamic SS drawing & GenAtom? issues - now all OK
fixes to get SS structure factors to compute & twins in SS data issues

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 176.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2015-08-12 18:30:41 +0000 (Wed, 12 Aug 2015) $
6# $Author: vondreele $
7# $Revision: 1956 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1956 2015-08-12 18:30:41Z 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: 1956 $")
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        (BHT) correct?
2442        '''
2443        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2444            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2445            self.PatternTree.SetItemPyData(sub,[''])
2446        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2447            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2448            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2449        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2450            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2451            self.PatternTree.SetItemPyData(sub,{})
2452        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2453            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2454            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2455        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2456            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2457            self.PatternTree.SetItemPyData(sub,{})
2458        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2459            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2460            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2461                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2462               
2463    class CopyDialog(wx.Dialog):
2464        '''Creates a dialog for copying control settings between
2465        data tree items'''
2466        def __init__(self,parent,title,text,data):
2467            wx.Dialog.__init__(self,parent,-1,title, 
2468                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2469            self.data = data
2470            panel = wx.Panel(self)
2471            mainSizer = wx.BoxSizer(wx.VERTICAL)
2472            topLabl = wx.StaticText(panel,-1,text)
2473            mainSizer.Add((10,10),1)
2474            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2475            mainSizer.Add((10,10),1)
2476            ncols = len(data)/40+1
2477            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2478            for id,item in enumerate(self.data):
2479                ckbox = wx.CheckBox(panel,id,item[1])
2480                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2481                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2482            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2483            OkBtn = wx.Button(panel,-1,"Ok")
2484            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2485            cancelBtn = wx.Button(panel,-1,"Cancel")
2486            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2487            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2488            btnSizer.Add((20,20),1)
2489            btnSizer.Add(OkBtn)
2490            btnSizer.Add((20,20),1)
2491            btnSizer.Add(cancelBtn)
2492            btnSizer.Add((20,20),1)
2493           
2494            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2495            panel.SetSizer(mainSizer)
2496            panel.Fit()
2497            self.Fit()
2498       
2499        def OnCopyChange(self,event):
2500            id = event.GetId()
2501            self.data[id][0] = self.FindWindowById(id).GetValue()       
2502           
2503        def OnOk(self,event):
2504            parent = self.GetParent()
2505            parent.Raise()
2506            self.EndModal(wx.ID_OK)             
2507           
2508        def OnCancel(self,event):
2509            parent = self.GetParent()
2510            parent.Raise()
2511            self.EndModal(wx.ID_CANCEL)             
2512           
2513        def GetData(self):
2514            return self.data
2515       
2516    class SumDialog(wx.Dialog):
2517        'Allows user to supply scale factor(s) when summing data'
2518        def __init__(self,parent,title,text,dataType,data):
2519            wx.Dialog.__init__(self,parent,-1,title, 
2520                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2521            self.data = data
2522            panel = wx.Panel(self)
2523            mainSizer = wx.BoxSizer(wx.VERTICAL)
2524            topLabl = wx.StaticText(panel,-1,text)
2525            mainSizer.Add((10,10),1)
2526            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2527            mainSizer.Add((10,10),1)
2528            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2529            for id,item in enumerate(self.data[:-1]):
2530                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2531                name.SetEditable(False)
2532                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2533                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2534                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2535                dataGridSizer.Add(scale,0,wx.LEFT,10)
2536                dataGridSizer.Add(name,0,wx.RIGHT,10)
2537            if dataType:
2538                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2539                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2540                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2541                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2542                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2543                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2544            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2545            OkBtn = wx.Button(panel,-1,"Ok")
2546            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2547            cancelBtn = wx.Button(panel,-1,"Cancel")
2548            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2549            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2550            btnSizer.Add((20,20),1)
2551            btnSizer.Add(OkBtn)
2552            btnSizer.Add((20,20),1)
2553            btnSizer.Add(cancelBtn)
2554            btnSizer.Add((20,20),1)
2555           
2556            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2557            panel.SetSizer(mainSizer)
2558            panel.Fit()
2559            self.Fit()
2560
2561        def OnScaleChange(self,event):
2562            id = event.GetId()
2563            value = self.FindWindowById(id).GetValue()
2564            try:
2565                self.data[id][0] = float(value)
2566                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2567            except ValueError:
2568                if value and '-' not in value[0]:
2569                    print 'bad input - numbers only'
2570                    self.FindWindowById(id).SetValue('0.000')
2571           
2572        def OnNameChange(self,event):
2573            self.data[-1] = self.name.GetValue() 
2574           
2575        def OnOk(self,event):
2576            parent = self.GetParent()
2577            parent.Raise()
2578            self.EndModal(wx.ID_OK)             
2579           
2580        def OnCancel(self,event):
2581            parent = self.GetParent()
2582            parent.Raise()
2583            self.EndModal(wx.ID_CANCEL)             
2584           
2585        def GetData(self):
2586            return self.data
2587                       
2588    def OnPwdrSum(self,event):
2589        'Sum together powder data(?)'
2590        TextList = []
2591        DataList = []
2592        SumList = []
2593        Names = []
2594        Inst = None
2595        SumItemList = []
2596        Comments = ['Sum equals: \n']
2597        if self.PatternTree.GetCount():
2598            item, cookie = self.PatternTree.GetFirstChild(self.root)
2599            while item:
2600                name = self.PatternTree.GetItemText(item)
2601                Names.append(name)
2602                if 'PWDR' in name:
2603                    TextList.append([0.0,name])
2604                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2605                    if not Inst:
2606                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2607                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2608            if len(TextList) < 2:
2609                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2610                return
2611            TextList.append('default_sum_name')               
2612            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2613            try:
2614                if dlg.ShowModal() == wx.ID_OK:
2615                    lenX = 0
2616                    Xminmax = [0,0]
2617                    Xsum = []
2618                    Ysum = []
2619                    Vsum = []
2620                    result = dlg.GetData()
2621                    for i,item in enumerate(result[:-1]):
2622                        scale,name = item
2623                        data = DataList[i]
2624                        if scale:
2625                            Comments.append("%10.3f %s" % (scale,' * '+name))
2626                            x,y,w,yc,yb,yd = data   #numpy arrays!
2627                            v = 1./w
2628                            if lenX:
2629                                if lenX != len(x):
2630                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2631                                        '\nExpected:'+str(lenX)+ \
2632                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2633                                    return
2634                            else:
2635                                lenX = len(x)
2636                            if Xminmax[1]:
2637                                if Xminmax != [x[0],x[-1]]:
2638                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2639                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2640                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2641                                    return
2642                                else:
2643                                    for j,yi in enumerate(y):
2644                                         Ysum[j] += scale*yi
2645                                         Vsum[j] += abs(scale)*v[j]
2646                            else:
2647                                Xminmax = [x[0],x[-1]]
2648                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2649                                for j,yi in enumerate(y):
2650                                    Xsum.append(x[j])
2651                                    Ysum.append(scale*yi)
2652                                    Vsum.append(abs(scale*v[j]))
2653                    Wsum = 1./np.array(Vsum)
2654                    outname = 'PWDR '+result[-1]
2655                    Id = 0
2656                    if outname in Names:
2657                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2658                        try:
2659                            if dlg2.ShowModal() == wx.ID_OK:
2660                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2661                                self.PatternTree.Delete(Id)
2662                        finally:
2663                            dlg2.Destroy()
2664                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2665                    if Id:
2666                        Sample = G2pdG.SetDefaultSample()
2667                        valuesdict = {
2668                            'wtFactor':1.0,
2669                            'Dummy':False,
2670                            'ranId':ran.randint(0,sys.maxint),
2671                            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
2672                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2673                            }
2674                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2675                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2676                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2677                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2678                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2679                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2680                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2681                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2682                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2683                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2684                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2685                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2686                        self.PatternTree.SelectItem(Id)
2687                        self.PatternTree.Expand(Id)
2688            finally:
2689                dlg.Destroy()
2690
2691    def OnImageSum(self,event):
2692        'Sum together image data(?)'
2693        TextList = []
2694        DataList = []
2695        SumList = []
2696        Names = []
2697        Inst = []
2698        SumItemList = []
2699        Comments = ['Sum equals: \n']
2700        if self.PatternTree.GetCount():
2701            item, cookie = self.PatternTree.GetFirstChild(self.root)
2702            while item:
2703                name = self.PatternTree.GetItemText(item)
2704                Names.append(name)
2705                if 'IMG' in name:
2706                    TextList.append([0.0,name])
2707                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2708                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2709                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2710            if len(TextList) < 2:
2711                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2712                return
2713            TextList.append('default_sum_name')               
2714            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2715            try:
2716                if dlg.ShowModal() == wx.ID_OK:
2717                    imSize = 0
2718                    result = dlg.GetData()
2719                    First = True
2720                    Found = False
2721                    for i,item in enumerate(result[:-1]):
2722                        scale,name = item
2723                        data = DataList[i]
2724                        if scale:
2725                            Found = True                               
2726                            Comments.append("%10.3f %s" % (scale,' * '+name))
2727                            Npix,imagefile = data
2728                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2729                            if First:
2730                                newImage = np.zeros_like(image)
2731                                First = False
2732                            if imSize:
2733                                if imSize != Npix:
2734                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2735                                        '\nExpected:'+str(imSize)+ \
2736                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2737                                    return
2738                                newImage = newImage+scale*image
2739                            else:
2740                                imSize = Npix
2741                                newImage = newImage+scale*image
2742                            del(image)
2743                    if not Found:
2744                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2745                        return
2746                       
2747                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2748                    outname = 'IMG '+result[-1]
2749                    Id = 0
2750                    if outname in Names:
2751                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2752                        try:
2753                            if dlg2.ShowModal() == wx.ID_OK:
2754                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2755                        finally:
2756                            dlg2.Destroy()
2757                    else:
2758                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2759                    if Id:
2760                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2761                            'G2img files (*.G2img)|*.G2img', 
2762                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2763                        if dlg.ShowModal() == wx.ID_OK:
2764                            newimagefile = dlg.GetPath()
2765                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2766                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2767                            Imax = np.amax(newImage)
2768                            Imin = np.amin(newImage)
2769                            newImage = []
2770                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2771                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2772                        del(newImage)
2773                        if self.imageDefault:
2774                            Data = copy.copy(self.imageDefault)
2775                        Data['showLines'] = True
2776                        Data['ring'] = []
2777                        Data['rings'] = []
2778                        Data['cutoff'] = 10
2779                        Data['pixLimit'] = 20
2780                        Data['ellipses'] = []
2781                        Data['calibrant'] = ''
2782                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2783                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2784                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2785                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2786                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2787                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2788                        self.PatternTree.SelectItem(Id)
2789                        self.PatternTree.Expand(Id)
2790                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2791                        self.Image = self.PickId
2792            finally:
2793                dlg.Destroy()
2794                     
2795    def OnAddPhase(self,event):
2796        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2797        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2798            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2799        else:
2800            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2801        PhaseName = ''
2802        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2803            style=wx.OK)
2804        if dlg.ShowModal() == wx.ID_OK:
2805            PhaseName = dlg.GetValue()
2806        dlg.Destroy()
2807        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2808        E,SGData = G2spc.SpcGroup('P 1')
2809        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2810       
2811    def OnDeletePhase(self,event):
2812        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2813        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2814        if self.dataFrame:
2815            self.dataFrame.Clear() 
2816        TextList = []
2817        DelList = []
2818        DelItemList = []
2819        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2820            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2821        else:
2822            return
2823        if sub:
2824            item, cookie = self.PatternTree.GetFirstChild(sub)
2825            while item:
2826                TextList.append(self.PatternTree.GetItemText(item))
2827                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2828            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2829            try:
2830                if dlg.ShowModal() == wx.ID_OK:
2831                    result = dlg.GetSelections()
2832                    for i in result: DelList.append([i,TextList[i]])
2833                    item, cookie = self.PatternTree.GetFirstChild(sub)
2834                    i = 0
2835                    while item:
2836                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2837                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2838                        i += 1
2839                    for item in DelItemList:
2840                        name = self.PatternTree.GetItemText(item)
2841                        self.PatternTree.Delete(item)
2842                        self.G2plotNB.Delete(name)
2843                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2844                    while item:
2845                        name = self.PatternTree.GetItemText(item)
2846                        if 'PWDR' in name:
2847                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2848                            refList = self.PatternTree.GetItemPyData(Id)
2849                            if len(refList):
2850                                for i,item in DelList:
2851                                    if item in refList:
2852                                        del(refList[item])
2853                            self.PatternTree.SetItemPyData(Id,refList)
2854                        elif 'HKLF' in name:
2855                            data = self.PatternTree.GetItemPyData(item)
2856                            data[0] = {}
2857                            self.PatternTree.SetItemPyData(item,data)
2858                           
2859                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2860            finally:
2861                dlg.Destroy()
2862               
2863    def OnRenameData(self,event):
2864        'Renames an existing phase. Called by Data/Rename Phase menu'
2865        name = self.PatternTree.GetItemText(self.PickId)     
2866        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2867            dataType = name[:name.index(' ')+1]                 #includes the ' '
2868            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2869                defaultValue=name[name.index(' ')+1:])
2870            try:
2871                if dlg.ShowModal() == wx.ID_OK:
2872                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2873            finally:
2874                dlg.Destroy()
2875       
2876    def GetFileList(self,fileType,skip=None):        #potentially useful?
2877        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2878        fileList = []
2879        Source = ''
2880        id, cookie = self.PatternTree.GetFirstChild(self.root)
2881        while id:
2882            name = self.PatternTree.GetItemText(id)
2883            if fileType in name:
2884                if id == skip:
2885                    Source = name
2886                else:
2887                    fileList.append([False,name,id])
2888            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2889        if skip:
2890            return fileList,Source
2891        else:
2892            return fileList
2893           
2894    def OnDataDelete(self, event):
2895        '''Delete one or more histograms from data tree. Called by the
2896        Data/DeleteData menu
2897        '''
2898        TextList = []
2899        DelList = []
2900        DelItemList = []
2901        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
2902        ifPWDR = False
2903        ifSASD = False
2904        ifIMG = False
2905        ifHKLF = False
2906        ifPDF = False
2907        if self.PatternTree.GetCount():
2908            item, cookie = self.PatternTree.GetFirstChild(self.root)
2909            while item:
2910                name = self.PatternTree.GetItemText(item)
2911                if name not in ['Notebook','Controls','Covariance','Constraints',
2912                    'Restraints','Phases','Rigid bodies','Sequential results']:
2913                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
2914                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
2915                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
2916                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
2917                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
2918                    TextList.append(name)
2919                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2920            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2921            try:
2922                if dlg.ShowModal() == wx.ID_OK:
2923                    result = dlg.GetSelections()
2924                    for i in result: DelList.append(TextList[i])
2925                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2926                    while item:
2927                        itemName = self.PatternTree.GetItemText(item)
2928                        if itemName in DelList:
2929                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
2930                            elif 'SASD' in itemName: nItems['SASD'] -= 1
2931                            elif 'IMG' in itemName: nItems['IMG'] -= 1
2932                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
2933                            elif 'PDF' in itemName: nItems['PDF'] -= 1
2934                            DelItemList.append(item)
2935                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2936                    for item in DelItemList:
2937                        self.PatternTree.Delete(item)
2938                    self.PickId = 0
2939                    self.PickIdText = None
2940                    self.PatternId = 0
2941                    if nItems['PWDR']:
2942                        wx.CallAfter(G2plt.PlotPatterns,self,True)
2943                    else:
2944                        self.G2plotNB.Delete('Powder Patterns')
2945                    if not nItems['IMG']:
2946                        self.G2plotNB.Delete('2D Powder Image')
2947                    if not nItems['HKLF']:
2948                        self.G2plotNB.Delete('Structure Factors')
2949                        if '3D Structure Factors' in self.G2plotNB.plotList:
2950                            self.G2plotNB.Delete('3D Structure Factors')
2951            finally:
2952                dlg.Destroy()
2953
2954    def OnFileOpen(self, event, filename=None):
2955        '''Gets a GSAS-II .gpx project file in response to the
2956        File/Open Project menu button
2957        '''
2958        result = wx.ID_OK
2959        self.EnablePlot = False
2960        if self.PatternTree.GetChildrenCount(self.root,False):
2961            if self.dataFrame:
2962                self.dataFrame.Clear() 
2963            dlg = wx.MessageDialog(
2964                self,
2965                'Do you want to overwrite the current project? '
2966                'Any unsaved changes will be lost. Press OK to continue.',
2967                'Overwrite?',  wx.OK | wx.CANCEL)
2968            try:
2969                result = dlg.ShowModal()
2970                if result == wx.ID_OK:
2971                    self.PatternTree.DeleteChildren(self.root)
2972                    self.GSASprojectfile = ''
2973                    if self.HKL: self.HKL = []
2974                    if self.G2plotNB.plotList:
2975                        self.G2plotNB.clear()
2976            finally:
2977                dlg.Destroy()
2978        if result != wx.ID_OK: return
2979
2980        if not filename:
2981            if self.dataDisplay: self.dataDisplay.Destroy()
2982            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2983                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2984            try:
2985                if dlg.ShowModal() != wx.ID_OK: return
2986                self.GSASprojectfile = dlg.GetPath()
2987                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2988                self.dirname = dlg.GetDirectory()
2989            finally:
2990                dlg.Destroy()
2991        else:
2992            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2993            self.dirname = os.path.split(filename)[0]
2994
2995        try:
2996            self.StartProject()         #open the file if possible
2997        except:
2998            print '\nError opening file ',filename
2999            import traceback
3000            print traceback.format_exc()
3001       
3002    def StartProject(self):
3003        '''Opens a GSAS-II project file & selects the 1st available data set to
3004        display (PWDR, HKLF or SASD)
3005        '''
3006       
3007        Id = 0
3008        G2IO.ProjFileOpen(self)
3009        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3010        self.PatternTree.Expand(self.root)
3011        self.HKL = []
3012        item, cookie = self.PatternTree.GetFirstChild(self.root)
3013        while item and not Id:
3014            name = self.PatternTree.GetItemText(item)
3015            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3016                Id = item
3017            elif name == 'Controls':
3018                data = self.PatternTree.GetItemPyData(item)
3019                if data:
3020                    for item in self.Refine: item.Enable(True)
3021                    self.EnableSeqRefineMenu()
3022            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3023        if Id:
3024            self.EnablePlot = True
3025            self.PatternTree.SelectItem(Id)
3026        self.CheckNotebook()
3027        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3028
3029    def OnFileClose(self, event):
3030        '''Clears the data tree in response to the
3031        File/New Project menu button. User is given option to save
3032        the project.
3033        '''
3034        if self.dataFrame:
3035            self.dataFrame.Clear()
3036            self.dataFrame.SetLabel('GSAS-II data display') 
3037        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3038        try:
3039            result = dlg.ShowModal()
3040            if result == wx.ID_OK:
3041                self.OnFileSaveMenu(event)
3042            if result != wx.ID_CANCEL:
3043                self.GSASprojectfile = ''
3044                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3045                self.PatternTree.DeleteChildren(self.root)
3046                if self.HKL: self.HKL = []
3047                if self.G2plotNB.plotList:
3048                    self.G2plotNB.clear()
3049        finally:
3050            dlg.Destroy()
3051
3052    def OnFileSave(self, event):
3053        '''Save the current project in response to the
3054        File/Save Project menu button
3055        '''
3056       
3057        if self.GSASprojectfile: 
3058            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
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                G2IO.ProjFileSave(self)
3076                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3077        finally:
3078            dlg.Destroy()
3079
3080    def ExitMain(self, event):
3081        '''Called if the main window is closed'''
3082        if self.undofile:
3083            os.remove(self.undofile)
3084        sys.exit()
3085       
3086    def OnFileExit(self, event):
3087        '''Called in response to the File/Quit menu button'''
3088        if self.dataFrame:
3089            self.dataFrame.Clear() 
3090            self.dataFrame.Destroy()
3091        self.Close()
3092       
3093    def OnExportPeakList(self,event):
3094        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
3095            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3096        try:
3097            if dlg.ShowModal() == wx.ID_OK:
3098                self.peaklistfile = dlg.GetPath()
3099                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3100                file = open(self.peaklistfile,'w')               
3101                item, cookie = self.PatternTree.GetFirstChild(self.root)
3102                while item:
3103                    name = self.PatternTree.GetItemText(item)
3104                    if 'PWDR' in name:
3105                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3106                        while item2:
3107                            name2 = self.PatternTree.GetItemText(item2)
3108                            if name2 == 'Peak List':
3109                                peaks = self.PatternTree.GetItemPyData(item2)['peaks']
3110                                file.write("%s \n" % (name+' Peak List'))
3111                                if len(peaks[0]) == 8:
3112                                    file.write('%10s %12s %10s %10s %10s\n'%('pos','int','sig','gam','FWHM'))
3113                                else:
3114                                    file.write('%10s %12s %10s %10s %10s %10s %10s\n'%('pos','int','alp','bet','sig','gam','FWHM'))                                   
3115                                for peak in peaks:
3116                                    if len(peak) == 8:  #CW
3117                                        FWHM = 2.*G2pwd.getgamFW(peak[6],peak[4])      #to get delta-2-theta in deg. from Gam(peak)
3118                                        file.write("%10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3119                                            (peak[0],peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3120                                    else:               #TOF - more cols
3121                                        FWHM = 2.*G2pwd.getgamFW(peak[10],peak[8])      #to get delta-TOF from Gam(peak)
3122                                        file.write("%10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3123                                            (peak[0],peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3124                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3125                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3126                file.close()
3127        finally:
3128            dlg.Destroy()
3129       
3130    def OnExportHKL(self,event):
3131        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
3132            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3133        try:
3134            if dlg.ShowModal() == wx.ID_OK:
3135                self.peaklistfile = dlg.GetPath()
3136                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3137                file = open(self.peaklistfile,'w')               
3138                item, cookie = self.PatternTree.GetFirstChild(self.root)
3139                while item:
3140                    name = self.PatternTree.GetItemText(item)
3141                    if 'PWDR' in name:
3142                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3143                        while item2:
3144                            name2 = self.PatternTree.GetItemText(item2)
3145                            if name2 == 'Reflection Lists':
3146                                data = self.PatternTree.GetItemPyData(item2)
3147                                phases = data.keys()
3148                                for phase in phases:
3149                                    peaks = data[phase]
3150                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3151                                    if 'T' in peaks.get('Type','PXC'):
3152                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3153                                    else:               
3154                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3155                                    for peak in peaks['RefList']:
3156                                        FWHM = 2.*G2pwd.getgamFW(peak[7],peak[6])
3157                                        if 'T' in peaks.get('Type','PXC'):
3158                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3159                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3160                                        else:
3161                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3162                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3163                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3164                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3165                file.close()
3166        finally:
3167            dlg.Destroy()
3168       
3169    def OnExportPDF(self,event):
3170        #need S(Q) and G(R) to be saved here - probably best from selection?
3171        names = ['All']
3172        exports = []
3173        item, cookie = self.PatternTree.GetFirstChild(self.root)
3174        while item:
3175            name = self.PatternTree.GetItemText(item)
3176            if 'PDF' in name:
3177                names.append(name)
3178            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3179        if names:
3180            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3181            if dlg.ShowModal() == wx.ID_OK:
3182                sel = dlg.GetSelections()
3183                if sel[0] == 0:
3184                    exports = names[1:]
3185                else:
3186                    for x in sel:
3187                        exports.append(names[x])
3188            dlg.Destroy()
3189        if exports:
3190            G2IO.PDFSave(self,exports)
3191       
3192    def OnMakePDFs(self,event):
3193        '''Calculates PDFs
3194        '''
3195        sind = lambda x: math.sin(x*math.pi/180.)
3196        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3197        TextList = ['All PWDR']
3198        PDFlist = []
3199        Names = []
3200        if self.PatternTree.GetCount():
3201            id, cookie = self.PatternTree.GetFirstChild(self.root)
3202            while id:
3203                name = self.PatternTree.GetItemText(id)
3204                Names.append(name)
3205                if 'PWDR' in name:
3206                    TextList.append(name)
3207                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3208            if len(TextList) == 1:
3209                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3210                return
3211            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3212            try:
3213                if dlg.ShowModal() == wx.ID_OK:
3214                    result = dlg.GetSelections()
3215                    for i in result: PDFlist.append(TextList[i])
3216                    if 0 in result:
3217                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3218                    for item in PDFlist:
3219                        PWDRname = item[4:]
3220                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3221                        Data = {
3222                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3223                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3224                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3225                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3226                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3227                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3228                            'Lorch':True,}
3229                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3230                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3231                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3232                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3233                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3234                for item in self.ExportPDF: item.Enable(True)
3235            finally:
3236                dlg.Destroy()
3237               
3238    def GetPWDRdatafromTree(self,PWDRname):
3239        ''' Returns powder data from GSASII tree
3240
3241        :param str PWDRname: a powder histogram name as obtained from
3242          :meth:`GSASIIstruct.GetHistogramNames`
3243
3244        :returns: PWDRdata = powder data dictionary with
3245          Powder data arrays, Limits, Instrument Parameters,
3246          Sample Parameters           
3247        '''
3248        PWDRdata = {}
3249        try:
3250            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3251        except ValueError:
3252            PWDRdata['wtFactor'] = 1.0
3253        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3254        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3255        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3256        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3257        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3258        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3259        if 'ranId' not in PWDRdata:  # patch, add a random Id
3260            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3261        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3262            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3263        return PWDRdata
3264
3265    def GetHKLFdatafromTree(self,HKLFname):
3266        ''' Returns single crystal data from GSASII tree
3267
3268        :param str HKLFname: a single crystal histogram name as obtained
3269          from
3270          :meth:`GSASIIstruct.GetHistogramNames`
3271
3272        :returns: HKLFdata = single crystal data list of reflections
3273
3274        '''
3275        HKLFdata = {}
3276        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3277#        try:
3278#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3279#        except ValueError:
3280#            HKLFdata['wtFactor'] = 1.0
3281        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3282        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3283        return HKLFdata
3284       
3285    def GetPhaseData(self):
3286        '''Returns a dict with defined phases.
3287        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3288        get same info from GPX file.
3289        '''
3290        phaseData = {}
3291        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3292            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3293        else:
3294            print 'no phases found in GetPhaseData'
3295            sub = None
3296        if sub:
3297            item, cookie = self.PatternTree.GetFirstChild(sub)
3298            while item:
3299                phaseName = self.PatternTree.GetItemText(item)
3300                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3301                if 'ranId' not in phaseData[phaseName]:
3302                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3303                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3304        return phaseData
3305
3306    def GetPhaseInfofromTree(self):
3307        '''Get the phase names and their rId values,
3308        also the histograms used in each phase.
3309
3310        :returns: (phaseRIdList, usedHistograms) where
3311
3312          * phaseRIdList is a list of random Id values for each phase
3313          * usedHistograms is a dict where the keys are the phase names
3314            and the values for each key are a list of the histogram names
3315            used in each phase.
3316        '''
3317        phaseRIdList = []
3318        usedHistograms = {}
3319        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3320        if sub:
3321            item, cookie = self.PatternTree.GetFirstChild(sub)
3322            while item:
3323                phaseName = self.PatternTree.GetItemText(item)
3324                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3325                if ranId: phaseRIdList.append(ranId)
3326                data = self.PatternTree.GetItemPyData(item)
3327                UseList = data['Histograms']
3328                usedHistograms[phaseName] = UseList.keys()
3329                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3330        return phaseRIdList,usedHistograms
3331
3332    def GetPhaseNames(self):
3333        '''Returns a list of defined phases.
3334        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3335        get same info from GPX file.
3336        '''
3337        phaseNames = []
3338        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3339            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3340        else:
3341            print 'no phases found in GetPhaseNames'
3342            sub = None
3343        if sub:
3344            item, cookie = self.PatternTree.GetFirstChild(sub)
3345            while item:
3346                phase = self.PatternTree.GetItemText(item)
3347                phaseNames.append(phase)
3348                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3349        return phaseNames
3350   
3351    def GetHistogramNames(self,hType):
3352        """ Returns a list of histogram names found in the GSASII data tree
3353        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3354        get same info from GPX file.
3355       
3356        :param str hType: list of histogram types
3357        :return: list of histogram names
3358       
3359        """
3360        HistogramNames = []
3361        if self.PatternTree.GetCount():
3362            item, cookie = self.PatternTree.GetFirstChild(self.root)
3363            while item:
3364                name = self.PatternTree.GetItemText(item)
3365                if name[:4] in hType:
3366                    HistogramNames.append(name)       
3367                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3368
3369        return HistogramNames
3370                   
3371    def GetUsedHistogramsAndPhasesfromTree(self):
3372        ''' Returns all histograms that are found in any phase
3373        and any phase that uses a histogram.
3374        This also assigns numbers to used phases and histograms by the
3375        order they appear in the file.
3376        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3377        get same info from GPX file.
3378
3379        :returns: (Histograms,Phases)
3380
3381            * Histograms = dictionary of histograms as {name:data,...}
3382            * Phases = dictionary of phases that use histograms
3383        '''
3384        Histograms = {}
3385        Phases = {}
3386        phaseNames = self.GetPhaseNames()
3387        phaseData = self.GetPhaseData()
3388        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3389
3390        for phase in phaseData:
3391            Phase = phaseData[phase]
3392            pId = phaseNames.index(phase)
3393            Phase['pId'] = pId
3394            if Phase['Histograms']:
3395                if phase not in Phases:
3396                    Phases[phase] = Phase
3397                for hist in Phase['Histograms']:
3398                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3399                        Phase['Histograms'][hist]['Use'] = True         
3400                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3401                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3402                        if item:
3403                            if 'PWDR' in hist[:4]: 
3404                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3405                            elif 'HKLF' in hist[:4]:
3406                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3407                            hId = histoList.index(hist)
3408                            Histograms[hist]['hId'] = hId
3409                        else: # would happen if a referenced histogram were renamed or deleted
3410                            print('For phase "'+str(phase)+
3411                                  '" unresolved reference to histogram "'+str(hist)+'"')
3412        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3413        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3414        return Histograms,Phases
3415       
3416    def MakeLSParmDict(self):
3417        '''Load all parameters used for computation from the tree into a
3418        dict of paired values [value, refine flag]. Note that this is
3419        different than the parmDict used in the refinement, which only has
3420        values.
3421
3422        Note that similar things are done in
3423        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3424        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3425        a GPX file).
3426
3427        :returns: (parmDict,varyList) where:
3428
3429         * parmDict is a dict with values and refinement flags
3430           for each parameter and
3431         * varyList is a list of variables (refined parameters).
3432        '''
3433        parmDict = {}
3434        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3435        for phase in Phases:
3436            if 'pId' not in Phases[phase]:
3437                self.ErrorDialog('View parameter error','You must run least squares at least once')
3438                raise Exception,'No pId for phase '+str(phase)
3439        rigidbodyDict = self.PatternTree.GetItemPyData(   
3440            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3441        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3442        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3443        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3444        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3445        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3446        varyList = rbVary+phaseVary+hapVary+histVary
3447        parmDict.update(rbDict)
3448        parmDict.update(phaseDict)
3449        parmDict.update(hapDict)
3450        parmDict.update(histDict)
3451        for parm in parmDict:
3452            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3453                'Omega','Chi','Phi','nDebye','nPeaks']:
3454                parmDict[parm] = [parmDict[parm],'-']
3455            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3456                parmDict[parm] = [parmDict[parm],'-']
3457            elif parm in varyList:
3458                parmDict[parm] = [parmDict[parm],'T']
3459            else:
3460                parmDict[parm] = [parmDict[parm],'F']
3461        # for i in parmDict: print i,'\t',parmDict[i]
3462        # fl = open('parmDict.dat','wb')
3463        # import cPickle
3464        # cPickle.dump(parmDict,fl,1)
3465        # fl.close()
3466        return parmDict,varyList
3467
3468    def ShowLSParms(self,event):
3469        '''Displays a window showing all parameters in the refinement.
3470        Called from the Calculate/View LS Parms menu.
3471        '''
3472        parmDict,varyList = self.MakeLSParmDict()
3473        parmValDict = {}
3474        for i in parmDict:
3475            parmValDict[i] = parmDict[i][0]
3476           
3477        reqVaryList = tuple(varyList) # save requested variables
3478        try:
3479            # process constraints
3480            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3481            Constraints = self.PatternTree.GetItemPyData(sub)
3482            constList = []
3483            for item in Constraints:
3484                if item.startswith('_'): continue
3485                constList += Constraints[item]
3486            G2mv.InitVars()
3487            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3488            groups,parmlist = G2mv.GroupConstraints(constrDict)
3489            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3490            G2mv.Map2Dict(parmValDict,varyList)
3491        except:
3492            pass
3493        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3494        dlg.ShowModal()
3495        dlg.Destroy()
3496       
3497    def OnRefine(self,event):
3498        '''Perform a refinement.
3499        Called from the Calculate/Refine menu.
3500        '''       
3501        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3502        if Id:
3503            dlg = wx.MessageDialog(
3504                self,
3505                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3506                'Remove sequential results?',wx.OK|wx.CANCEL)
3507            if dlg.ShowModal() == wx.ID_OK:
3508                self.PatternTree.Delete(Id)
3509                dlg.Destroy()
3510            else:
3511                dlg.Destroy()
3512                return
3513        self.OnFileSave(event)
3514        # check that constraints are OK here
3515        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3516        if errmsg:
3517            self.ErrorDialog('Refinement error',errmsg)
3518            return
3519        if warnmsg:
3520            print('Conflict between refinment flag settings and constraints:\n'+
3521                warnmsg+'\nRefinement not possible')
3522            self.ErrorDialog('Refinement Flag Error',
3523                'Conflict between refinement flag settings and constraints:\n'+
3524                warnmsg+'\nRefinement not possible')
3525            return
3526        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3527            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3528            parent=self)
3529        Size = dlg.GetSize()
3530        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3531            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3532        dlg.CenterOnParent()
3533        Rw = 100.00
3534        oldId =  self.PatternTree.GetSelection()        #retain current selection
3535        oldPath = self.GetTreeItemsList(oldId)
3536        parentName = ''
3537        oldName = self.PatternTree.GetItemText(oldId)
3538        parentId = self.PatternTree.GetItemParent(oldId)
3539        if parentId:
3540            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3541            if 'Phases' in parentName:
3542                tabId = self.dataDisplay.GetSelection()
3543        try:
3544            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)
3545        finally:
3546            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3547            dlg.Destroy()
3548            wx.Yield()
3549        if OK:
3550            Rw = Msg
3551            dlg2 = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3552            try:
3553                if dlg2.ShowModal() == wx.ID_OK:
3554                    Id = 0
3555                    self.PatternTree.DeleteChildren(self.root)
3556                    self.HKL = []
3557#                    if self.G2plotNB.plotList:
3558#                        self.G2plotNB.clear()
3559                    G2IO.ProjFileOpen(self)
3560                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3561                    while item and not Id:
3562                        name = self.PatternTree.GetItemText(item)
3563                        if name[:4] in ['PWDR','HKLF']:
3564                            Id = item
3565                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3566                    if parentName:
3567                        parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3568                        if parentId:
3569                            itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3570                        else:
3571                            itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3572                        self.PatternTree.SelectItem(itemId)
3573                    if 'Phases' in parentName:
3574                        data = self.PatternTree.GetItemPyData(itemId)
3575                        if data['Drawing']:
3576                            data['Drawing']['Atoms'] = []
3577                            self.dataDisplay.SetSelection(4)    #location of Drawing Data
3578                        self.dataDisplay.SetSelection(tabId)
3579                    elif Id:
3580                        self.PickIdText = None  #force reload of PickId contents
3581                        self.PatternTree.SelectItem(Id)
3582
3583            finally:
3584                dlg2.Destroy()
3585        else:
3586            self.ErrorDialog('Refinement error',Msg)
3587
3588    def OnSeqRefine(self,event):
3589        '''Perform a sequential refinement.
3590        Called from the Calculate/Sequential refine menu.
3591        '''       
3592        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3593        if not Id:
3594            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3595            self.PatternTree.SetItemPyData(Id,{})           
3596        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3597        Controls['ShowCell'] = True
3598        self.OnFileSave(event)
3599        # check that constraints are OK here
3600        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3601        if errmsg:
3602            self.ErrorDialog('Refinement error',errmsg)
3603            return
3604        if warnmsg:
3605            print('Conflict between refinment flag settings and constraints:\n'+
3606                  warnmsg+'\nRefinement not possible')
3607            self.ErrorDialog('Refinement Flag Error',
3608                             'Conflict between refinment flag settings and constraints:\n'+
3609                             warnmsg+'\nRefinement not possible')
3610            return
3611        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3612            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3613            parent=self)           
3614        Size = dlg.GetSize()
3615        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3616            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3617        dlg.CenterOnParent()
3618        try:
3619            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3620        finally:
3621            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3622            dlg.Destroy()
3623            wx.Yield()
3624        if OK:
3625            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3626            try:
3627                if dlg.ShowModal() == wx.ID_OK:
3628                    Id = 0
3629                    self.PickIdText = None  #force reload of PickId contents
3630                    self.PatternTree.DeleteChildren(self.root)
3631                    if len(self.HKL): self.HKL = []
3632                    if self.G2plotNB.plotList:
3633                        self.G2plotNB.clear()
3634                    G2IO.ProjFileOpen(self)
3635                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3636                    self.PatternTree.SelectItem(Id)
3637   
3638            finally:
3639                dlg.Destroy()
3640        else:
3641            self.ErrorDialog('Sequential refinement error',Msg)
3642       
3643    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3644        'Display an error message'
3645        result = None
3646        if parent is None:
3647            dlg = wx.MessageDialog(self, message, title,  wtype)
3648        else:
3649            dlg = wx.MessageDialog(parent, message, title,  wtype)
3650            dlg.CenterOnParent() # not working on Mac
3651        try:
3652            result = dlg.ShowModal()
3653        finally:
3654            dlg.Destroy()
3655        return result
3656
3657class GSASIImain(wx.App):
3658    '''Defines a wxApp for GSAS-II
3659
3660    Creates a wx frame (self.main) which contains the display of the
3661    data tree.
3662    '''
3663    def OnInit(self):
3664        '''Called automatically when the app is created.'''
3665        if '2.7' not in sys.version[:5]:
3666            dlg = wx.MessageDialog(None, 
3667                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version[:5],
3668                'Python version error',  wx.OK)
3669            try:
3670                result = dlg.ShowModal()
3671            finally:
3672                dlg.Destroy()
3673            sys.exit()
3674        self.main = GSASII(None)
3675        self.main.Show()
3676        self.SetTopWindow(self.main)
3677        # DEBUG: jump to sequential results
3678        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3679        #self.main.PatternTree.SelectItem(Id)
3680        # end DEBUG
3681        return True
3682    # def MacOpenFile(self, filename):
3683    #     '''Called on Mac every time a file is dropped on the app when it is running,
3684    #     treat this like a File/Open project menu action.
3685    #     Should be ignored on other platforms
3686    #     '''
3687    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3688    #     print 'MacOpen',filename
3689    #     if os.path.splitext(filename)[1] == '.py': return
3690    #     # end PATCH
3691    #     self.main.OnFileOpen(None,filename)
3692    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3693   
3694def main():
3695    '''Start up the GSAS-II application'''
3696    #application = GSASIImain() # don't redirect output, someday we
3697    # may want to do this if we can
3698    application = GSASIImain(0)
3699    if GSASIIpath.GetConfigValue('wxInspector'):
3700        import wx.lib.inspection as wxeye
3701        wxeye.InspectionTool().Show()
3702
3703    #application.main.OnRefine(None)
3704    application.MainLoop()
3705   
3706if __name__ == '__main__':
3707    # print versions
3708    print "Python module versions loaded:"
3709    print "python:     ",sys.version[:5]
3710    print "wxpython:   ",wx.__version__
3711    print "matplotlib: ",mpl.__version__
3712    print "numpy:      ",np.__version__
3713    print "scipy:      ",sp.__version__
3714    print "OpenGL:     ",ogl.__version__
3715    try:
3716        from PIL import Image
3717        try:
3718            from PIL import PILLOW_VERSION
3719            version = PILLOW_VERSION
3720        except:
3721            version = Image.VERSION
3722        print "pillow:     ",version
3723    except ImportError:
3724        try:
3725            import Image
3726            print "Image (PIL):",Image.VERSION
3727        except ImportError:
3728            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3729    try:
3730        import mkl
3731        print "Max threads ",mkl.get_max_threads()
3732    except:
3733        pass
3734    import platform
3735    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3736    #print "wxPython description",wx.PlatformInfo
3737    print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
3738    GSASIIpath.InvokeDebugOpts()
3739    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.