source: trunk/GSASII.py @ 1945

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

fix another new histo in new phase error
some cleanup in texture calculations - unneeded list generation

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