source: trunk/GSASII.py @ 1822

Last change on this file since 1822 was 1822, checked in by vondreele, 7 years ago

selections on tree now update properly
Refine now returns to whatever tree item was selected & which phase tab was selected

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