source: trunk/GSASII.py @ 1676

Last change on this file since 1676 was 1672, checked in by vondreele, 10 years ago

remove Offset, qPlot, etc from G2frame and put them in data[0] dict for each powder pattern. Thus, each pattern can have different positions for tic marks, diff curve, etc. Some can be qPlot and other 2-theta. Setting are saved if project is saved

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