source: trunk/GSASII.py @ 1828

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

more tweaks on phase data; keeps selected histogram across phase

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