source: trunk/GSASII.py @ 1997

Last change on this file since 1997 was 1997, checked in by toby, 7 years ago

Add API for direct image read (G2IO.ExportPowderList?) and powder exports w/o GUI (G2IO.ExportPowderList?); redo export to add new method (Writer)

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