source: trunk/GSASII.py @ 1512

Last change on this file since 1512 was 1512, checked in by toby, 8 years ago

implement logging and config storage

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