source: trunk/GSASII.py @ 1561

Last change on this file since 1561 was 1561, checked in by vondreele, 9 years ago

avoid error on startup with no file name
fix derivative errors for lattice parameters

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