source: trunk/GSASII.py @ 1170

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

remove Rigid bodies from delete data list
revise geometric correction for tilted detectors

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 131.1 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2013-12-17 18:06:10 +0000 (Tue, 17 Dec 2013) $
6# $Author: vondreele $
7# $Revision: 1170 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1170 2013-12-17 18:06:10Z vondreele $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. Please restart GSAS-II')
59    print('*******************************************************')         
60    sys.exit()
61
62# load the GSAS routines
63import GSASIIpath
64GSASIIpath.SetVersionNumber("$Revision: 1170 $")
65import GSASIIIO as G2IO
66import GSASIIgrid as G2gd
67import GSASIIplot as G2plt
68import GSASIIpwd as G2pwd
69import GSASIIpwdGUI as G2pdG
70import GSASIIspc as G2spc
71import GSASIIstrMain as G2stMn
72import GSASIIstrIO as G2stIO
73import GSASIImapvars as G2mv
74import GSASIIobj as G2obj
75
76#wx inspector - use as needed
77wxInspector = False
78
79# print versions
80print "Python module versions loaded:"
81print "python:     ",sys.version[:5]
82print "wxpython:   ",wx.__version__
83print "matplotlib: ",mpl.__version__
84print "numpy:      ",np.__version__
85print "scipy:      ",sp.__version__
86print "OpenGL:     ",ogl.__version__
87try:
88    import mkl
89    print "Max threads ",mkl.get_max_threads()
90except:
91    pass
92#    print "MKL module not present"
93__version__ = '0.2.0'
94G2gd.__version__ = __version__
95print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
96
97# useful degree trig functions
98sind = lambda x: math.sin(x*math.pi/180.)
99cosd = lambda x: math.cos(x*math.pi/180.)
100tand = lambda x: math.tan(x*math.pi/180.)
101asind = lambda x: 180.*math.asin(x)/math.pi
102acosd = lambda x: 180.*math.acos(x)/math.pi
103atan2d = lambda x,y: 180.*math.atan2(y,x)/math.pi
104
105def create(parent):
106    return GSASII(parent)
107
108class GSASII(wx.Frame):
109    '''Define the main GSAS-II frame and its associated menu items
110    '''
111    def _Add_FileMenuItems(self, parent):
112        item = parent.Append(
113            help='Open a GSAS-II project file (*.gpx)', id=wx.ID_ANY,
114            kind=wx.ITEM_NORMAL,text='&Open project...')
115        self.Bind(wx.EVT_MENU, self.OnFileOpen, id=item.GetId())
116        item = parent.Append(
117            help='Save project under current name', id=wx.ID_ANY,
118            kind=wx.ITEM_NORMAL,text='&Save project')
119        self.Bind(wx.EVT_MENU, self.OnFileSave, id=item.GetId())
120        item = parent.Append(
121            help='Save current project to new file', id=wx.ID_ANY,
122            kind=wx.ITEM_NORMAL,text='Save project as...')
123        self.Bind(wx.EVT_MENU, self.OnFileSaveas, id=item.GetId())
124        item = parent.Append(
125            help='Create empty new project, saving current is optional', id=wx.ID_ANY,
126            kind=wx.ITEM_NORMAL,text='&New project')
127        self.Bind(wx.EVT_MENU, self.OnFileClose, id=item.GetId())
128        item = parent.Append(
129            help='Exit from GSAS-II', id=wx.ID_ANY,
130            kind=wx.ITEM_NORMAL,text='&Exit')
131        self.Bind(wx.EVT_MENU, self.OnFileExit, id=item.GetId())
132       
133    def _Add_DataMenuItems(self,parent):
134        item = parent.Append(
135            help='',id=wx.ID_ANY,
136            kind=wx.ITEM_NORMAL,
137            text='Read image data...')
138        self.Bind(wx.EVT_MENU, self.OnImageRead, id=item.GetId())
139        item = parent.Append(
140            help='',id=wx.ID_ANY,
141            kind=wx.ITEM_NORMAL,
142            text='Read Powder Pattern Peaks...')
143        self.Bind(wx.EVT_MENU, self.OnReadPowderPeaks, id=item.GetId())
144        item = parent.Append(
145            help='',id=wx.ID_ANY,
146            kind=wx.ITEM_NORMAL,
147            text='Sum powder data')
148        self.Bind(wx.EVT_MENU, self.OnPwdrSum, id=item.GetId())
149        item = parent.Append(
150            help='',id=wx.ID_ANY,
151            kind=wx.ITEM_NORMAL,
152            text='Sum image data')
153        self.Bind(wx.EVT_MENU, self.OnImageSum, id=item.GetId())
154        item = parent.Append(
155            help='',id=wx.ID_ANY,
156            kind=wx.ITEM_NORMAL,
157            text='Add phase')
158        self.Bind(wx.EVT_MENU, self.OnAddPhase, id=item.GetId())
159        item = parent.Append(
160            help='',id=wx.ID_ANY,
161            kind=wx.ITEM_NORMAL,
162            text='Delete phase')
163        self.Bind(wx.EVT_MENU, self.OnDeletePhase, id=item.GetId())
164        item = parent.Append(
165            help='',id=wx.ID_ANY,
166            kind=wx.ITEM_NORMAL,
167            text='Rename data') 
168        self.Bind(wx.EVT_MENU, self.OnRenameData, id=item.GetId())
169        item = parent.Append(
170            help='',id=wx.ID_ANY,
171            kind=wx.ITEM_NORMAL,
172            text='Delete data')
173        self.Bind(wx.EVT_MENU, self.OnDataDelete, id=item.GetId())
174               
175    def _Add_CalculateMenuItems(self,parent):
176        item = parent.Append(help='Make new PDFs from selected powder patterns', 
177            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='Make new PDFs')
178        self.MakePDF.append(item)
179#        item.Enable(False)
180        self.Bind(wx.EVT_MENU, self.OnMakePDFs, id=item.GetId())
181       
182        item = parent.Append(help='View least squares parameters', 
183            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='&View LS parms')
184        self.Bind(wx.EVT_MENU, self.ShowLSParms, id=item.GetId())
185       
186        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
187            text='&Refine')
188        if len(self.Refine): # extend state for new menus to match main (on mac)
189            state = self.Refine[0].IsEnabled()
190        else:
191            state = False
192        item.Enable(state)
193        self.Refine.append(item)
194        self.Bind(wx.EVT_MENU, self.OnRefine, id=item.GetId())
195       
196        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
197            text='Sequential refine')
198        if len(self.SeqRefine): # extend state for new menus to match main (on mac)
199            state = self.SeqRefine[0].IsEnabled()
200        else:
201            state = False
202        item.Enable(state)
203        self.SeqRefine.append(item)
204        self.Bind(wx.EVT_MENU, self.OnSeqRefine, id=item.GetId())
205       
206    def _init_Imports(self):
207        '''import all the G2phase*.py & G2sfact*.py & G2pwd*.py files that
208        are found in the path
209        '''
210
211        self.ImportPhaseReaderlist = []
212        self._init_Import_routines('phase',self.ImportPhaseReaderlist,'Phase')
213        self.ImportSfactReaderlist = []
214        self._init_Import_routines('sfact',self.ImportSfactReaderlist,'Struct_Factor')
215        self.ImportPowderReaderlist = []
216        self._init_Import_routines('pwd',self.ImportPowderReaderlist,'Powder_Data')
217        self.ImportMenuId = {}
218
219    def _init_Import_routines(self,prefix,readerlist,errprefix):
220        '''import all the import readers matching the prefix
221        '''
222        #path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file
223        #pathlist = sys.path[:]
224        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
225        #path2GSAS2 = os.path.join(
226        #    os.path.dirname(os.path.realpath(__file__)), # location of this file
227        #    'imports')
228        pathlist = sys.path[:]
229        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
230
231        filelist = []
232        for path in pathlist:
233            for filename in glob.iglob(os.path.join(
234                path,
235                "G2"+prefix+"*.py")):
236                filelist.append(filename)   
237                #print 'debug: found',filename
238        filelist = sorted(list(set(filelist))) # remove duplicates
239        for filename in filelist:
240            path,rootname = os.path.split(filename)
241            pkg = os.path.splitext(rootname)[0]
242            try:
243                fp = None
244                fp, fppath,desc = imp.find_module(pkg,[path,])
245                pkg = imp.load_module(pkg,fp,fppath,desc)
246                for clss in inspect.getmembers(pkg): # find classes defined in package
247                    if clss[0].startswith('_'): continue
248                    if inspect.isclass(clss[1]):
249                        # check if we have the required methods
250                        for m in 'Reader','ExtensionValidator','ContentsValidator':
251                            if not hasattr(clss[1],m): break
252                            if not callable(getattr(clss[1],m)): break
253                        else:
254                            reader = clss[1]() # create an import instance
255                            readerlist.append(reader)
256            except AttributeError:
257                print 'Import_'+errprefix+': Attribute Error'+str(filename)
258                pass
259            except ImportError:
260                print 'Import_'+errprefix+': Error importing file'+str(filename)
261                pass
262            if fp: fp.close()
263
264    def OnImportGeneric(self,reader,readerlist,label,multiple=False,usedRanIdList=[]):
265        '''Used to import Phases, powder dataset or single
266        crystal datasets (structure factor tables) using reader objects
267        subclassed from :class:`GSASIIIO.ImportPhase`, :class:`GSASIIIO.ImportStructFactor`
268        or :class:`GSASIIIO.ImportPowderData`. If a reader is specified, only
269        that will be attempted, but if no reader is specified, every one
270        that is potentially compatible (by file extension) will
271        be tried on the selected file(s).
272
273        :param readerobject reader: This will be a reference to
274          a particular object to be used to read a file or None,
275          if every appropriate reader should be used.
276
277        :param list readerlist: a list of reader objects appropriate for
278          the current read attempt. At present, this will be either
279          self.ImportPhaseReaderlist, self.ImportSfactReaderlist or
280          self.ImportPowderReaderlist (defined in _init_Imports from
281          the files found in the path), but in theory this list could
282          be tailored. Used only when reader is None.
283
284        :param str label: string to place on the open file dialog:
285          Open `label` input file
286
287        :param bool multiple: True if multiple files can be selected
288          in the file dialog. False is default. At present True is used
289          only for reading of powder data.
290
291        :param list usedRanIdList: an optional list of random Ids that
292          have been used and should not be reused
293
294        :returns: a list of reader objects (rd_list) that were able
295          to read the specified file(s). This list may be empty.
296        '''
297        self.lastimport = ''
298        self.zipfile = None
299        singlereader = True
300        if reader is None:
301            singlereader = False
302            multiple = False
303            #print "use all formats"
304            choices = "any file (*.*)|*.*"
305            choices += "|zip archive (.zip)|*.zip"
306            extdict = {}
307            # compile a list of allowed extensions
308            for rd in readerlist:
309                fmt = rd.formatName
310                for extn in rd.extensionlist:
311                    if not extdict.get(extn): extdict[extn] = []
312                    extdict[extn] += [fmt,]
313            for extn in sorted(extdict.keys(),cmp=lambda x,y: cmp(x.lower(), y.lower())):
314                fmt = ''
315                for f in extdict[extn]:
316                    if fmt != "": fmt += ', '
317                    fmt += f
318                choices += "|" + fmt + " file (*" + extn + ")|*" + extn
319        else:
320            readerlist = [reader,]
321            # compile a list of allowed extensions
322            choices = reader.formatName + " file ("
323            w = ""
324            for extn in reader.extensionlist:
325                if w != "": w += ";"
326                w += "*" + extn
327            choices += w + ")|" + w
328            choices += "|zip archive (.zip)|*.zip"
329            if not reader.strictExtension:
330                choices += "|any file (*.*)|*.*"
331        # get the file(s)
332        if multiple:
333            mode = style=wx.OPEN | wx.CHANGE_DIR | wx.MULTIPLE
334        else:
335            mode = style=wx.OPEN | wx.CHANGE_DIR
336        dlg = wx.FileDialog(self, message="Choose "+label+" input file",
337            defaultFile="",wildcard=choices, style=mode)
338        try:
339            if dlg.ShowModal() == wx.ID_OK:
340                if multiple:
341                    filelist = dlg.GetPaths()
342                    if len(filelist) == 0: return []
343                else:
344                    filename = dlg.GetPath()
345                    filelist = [filename,]
346                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
347            else: # cancel was pressed
348                return []
349        finally:
350            dlg.Destroy()
351        rd_list = []
352        filelist1 = []
353        for filename in filelist:
354            # is this a zip file?
355            if os.path.splitext(filename)[1].lower() == '.zip':
356                extractedfiles = G2IO.ExtractFileFromZip(
357                    filename,parent=self,
358                    multipleselect=True)
359                if extractedfiles is None: continue # error or Cancel
360                if extractedfiles != filename:
361                    self.zipfile = filename # save zip name
362                    filelist1 += extractedfiles
363                    continue
364            filelist1.append(filename)
365        filelist = filelist1
366        for filename in filelist:
367            # is this a zip file?
368            if os.path.splitext(filename)[1].lower() == '.zip':
369                extractedfile = G2IO.ExtractFileFromZip(filename,parent=self)
370                if extractedfile is None: continue # error or Cancel
371                if extractedfile != filename:
372                    filename,self.zipfile = extractedfile,filename # now use the file that was created
373            # set what formats are compatible with this file
374            primaryReaders = []
375            secondaryReaders = []
376            for rd in readerlist:
377                flag = rd.ExtensionValidator(filename)
378                if flag is None: 
379                    secondaryReaders.append(rd)
380                elif flag:
381                    primaryReaders.append(rd)
382            if len(secondaryReaders) + len(primaryReaders) == 0:
383                self.ErrorDialog('No Format','No matching format for file '+filename)
384                return []
385
386            fp = None
387            msg = ''
388            try:
389                fp = open(filename,'Ur')
390                if len(filelist) == 1:
391                    # confirm we have the right file
392                    rdmsg = 'File '+str(filename)+' begins:\n\n'
393                    for i in range(3):
394                        rdmsg += fp.readline()
395                    rdmsg += '\n\nDo you want to read this file?'
396                    if not all([ord(c) < 128 and ord(c) != 0 for c in rdmsg]): # show only if ASCII
397                        rdmsg = 'File '+str(
398                            filename)+' is a binary file. Do you want to read this file?'
399                    result = wx.ID_NO
400                    # it would be better to use something that
401                    # would resize better, but this will do for now
402                    dlg = wx.MessageDialog(
403                        self, rdmsg,
404                        'Is this the file you want?', 
405                        wx.YES_NO | wx.ICON_QUESTION,
406                        )
407                    dlg.SetSize((700,300)) # does not resize on Mac
408                    try:
409                        result = dlg.ShowModal()
410                    finally:
411                        dlg.Destroy()
412                    if result == wx.ID_NO: return []
413                           
414                self.lastimport = filename
415                # try the file first with Readers that specify the
416                # file's extension and later with ones that merely allow it
417                errorReport = ''
418                for rd in primaryReaders+secondaryReaders:
419                    fp.seek(0)  # rewind
420                    rd.errors = "" # clear out any old errors
421                    if not rd.ContentsValidator(fp): # rejected on cursory check
422                        errorReport += "\n  "+rd.formatName + ' validator error'
423                        if rd.errors: 
424                            errorReport += ': '+rd.errors
425                        continue 
426                    repeat = True
427                    rdbuffer = {} # create temporary storage for file reader
428                    block = 0
429                    while repeat: # loop if the reader asks for another pass on the file
430                        block += 1
431                        repeat = False
432                        fp.seek(0)  # rewind
433                        rd.objname = os.path.basename(filename)
434                        flag = False
435                        try:
436                            flag = rd.Reader(filename,fp,self,
437                                             buffer=rdbuffer,
438                                             blocknum=block,
439                                             usedRanIdList=usedRanIdList,
440                                             )
441                        except rd.ImportException as detail:
442                            rd.errors += "\n  Read exception: "+str(detail)
443                        except Exception as detail:
444                            rd.errors += "\n  Unhandled read exception: "+str(detail)
445                            rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
446                        if flag: # this read succeeded
447                            rd_list.append(copy.deepcopy(rd)) # save the result before it is written over
448                            if rd.repeat:
449                                repeat = True
450                            continue
451                        errorReport += '\n'+rd.formatName + ' read error'
452                        if rd.errors:
453                            errorReport += ': '+rd.errors
454                    if rd_list: # read succeeded, was there a warning or any errors?
455                        if rd.warnings:
456                            self.ErrorDialog('Read Warning','The '+ rd.formatName+
457                                             ' reader reported a warning message:\n\n'+
458                                             rd.warnings)
459                        break # success in reading, try no further
460                else:
461                    if singlereader:
462                        self.ErrorDialog('Read Error','The '+ rd.formatName+
463                                         ' reader was not able to read file '+filename+msg+
464                                         '\n\nError message(s):\n'+errorReport
465                                         )
466                    else:
467                        self.ErrorDialog('Read Error','No reader was able to read file '+filename+msg+
468                                         '\n\nError messages:\n'+errorReport
469                                         )
470            except:
471                import traceback
472                print traceback.format_exc()
473                self.ErrorDialog('Open Error','Unexpected error trying to open or read file '+filename)
474            if fp: fp.close()
475        return rd_list
476
477    def _Add_ImportMenu_Phase(self,parent):
478        '''configure the Import Phase menus accord to the readers found in _init_Imports
479        '''
480        submenu = wx.Menu()
481        item = parent.AppendMenu(wx.ID_ANY, 'Phase',
482            submenu, help='Import phase data')
483        for reader in self.ImportPhaseReaderlist:
484            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
485                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
486            self.ImportMenuId[item.GetId()] = reader
487            self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
488        item = submenu.Append(wx.ID_ANY,
489                              help='Import phase data, use file to try to determine format',
490                              kind=wx.ITEM_NORMAL,
491                              text='guess format from file')
492        self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
493
494    def OnImportPhase(self,event):
495        '''Called in response to an Import/Phase/... menu item
496        to read phase information.
497        dict self.ImportMenuId is used to look up the specific
498        reader item associated with the menu item, which will be
499        None for the last menu item, which is the "guess" option
500        where all appropriate formats will be tried.
501        '''
502        # look up which format was requested
503        reqrdr = self.ImportMenuId.get(event.GetId())
504        # make a list of used phase ranId's
505        phaseRIdList = []
506        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
507        if sub:
508            item, cookie = self.PatternTree.GetFirstChild(sub)
509            while item:
510                phaseName = self.PatternTree.GetItemText(item)
511                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
512                if ranId: phaseRIdList.append(ranId)
513                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
514        rdlist = self.OnImportGeneric(reqrdr,
515                                  self.ImportPhaseReaderlist,
516                                  'phase',usedRanIdList=phaseRIdList)
517        if len(rdlist) == 0: return
518        # for now rdlist is only expected to have one element
519        # but this will allow multiple phases to be imported
520        self.CheckNotebook()
521        for rd in rdlist:
522            dlg = wx.TextEntryDialog( # allow editing of phase name
523                self, 'Enter the name for the new phase',
524                'Edit phase name', rd.Phase['General']['Name'],
525                style=wx.OK)
526            dlg.CenterOnParent()
527            if dlg.ShowModal() == wx.ID_OK:
528                rd.Phase['General']['Name'] = dlg.GetValue()
529            dlg.Destroy()
530            PhaseName = rd.Phase['General']['Name']
531            print 'Read phase '+str(PhaseName)+' from file '+str(self.lastimport)
532            if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
533                sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
534            else:
535                sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
536            psub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
537            self.PatternTree.SetItemPyData(psub,rd.Phase)
538            self.PatternTree.Expand(self.root) # make sure phases are seen
539            self.PatternTree.Expand(sub) 
540            self.PatternTree.Expand(psub)
541            if rd.Constraints:
542                sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') # was created in CheckNotebook if needed
543                Constraints = self.PatternTree.GetItemPyData(sub)               
544                # TODO: make sure that NEWVAR names are unique here?
545                for i in rd.Constraints:
546                    if type(i) is dict:
547                        #for j in i: print j,' --> ',i[j]
548                        if '_Explain' not in Constraints: Constraints['_Explain'] = {}
549                        Constraints['_Explain'].update(i)
550                        continue
551                    Constraints['Phase'].append(i)
552        return # success
553       
554    def _Add_ImportMenu_Sfact(self,parent):
555        '''configure the Import Structure Factor menus accord to the readers found in _init_Imports
556        '''
557        submenu = wx.Menu()
558        item = parent.AppendMenu(wx.ID_ANY, 'Structure Factor',
559            submenu, help='Import Structure Factor data')
560        for reader in self.ImportSfactReaderlist:
561            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,               
562                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
563            self.ImportMenuId[item.GetId()] = reader
564            self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
565        item = submenu.Append(wx.ID_ANY,
566            help='Import Structure Factor, use file to try to determine format',
567            kind=wx.ITEM_NORMAL,
568            text='guess format from file')
569        self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
570
571    def OnImportSfact(self,event):
572        '''Called in response to an Import/Structure Factor/... menu item
573        to read single crystal datasets.
574        dict self.ImportMenuId is used to look up the specific
575        reader item associated with the menu item, which will be
576        None for the last menu item, which is the "guess" option
577        where all appropriate formats will be tried.
578        '''
579        # look up which format was requested
580        reqrdr = self.ImportMenuId.get(event.GetId())
581        rdlist = self.OnImportGeneric(reqrdr,self.ImportSfactReaderlist,
582            'Structure Factor')
583        if len(rdlist) == 0: return
584        self.CheckNotebook()
585        for rd in rdlist:
586            HistName = rd.objname
587            if len(rdlist) <= 2: 
588                dlg = wx.TextEntryDialog( # allow editing of Structure Factor name
589                    self, 'Enter the name for the new Structure Factor',
590                    'Edit Structure Factor name', HistName,
591                    style=wx.OK)
592                dlg.CenterOnParent()
593                if dlg.ShowModal() == wx.ID_OK:
594                    HistName = dlg.GetValue()
595                dlg.Destroy()
596            print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
597            if not rd.RefDict['FF']:
598                rd.RefDict['FF'] = [{} for i in range(len(rd.RefDict['RefList']))]
599            Id = self.PatternTree.AppendItem(parent=self.root,
600                                             text='HKLF '+HistName)
601            valuesdict = {
602                'wtFactor':1.0,
603                'Dummy':False,
604                'ranId':ran.randint(0,sys.maxint),
605                }
606            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.RefDict])
607            Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
608            self.PatternTree.SetItemPyData(Sub,rd.Parameters)
609            self.PatternTree.SetItemPyData(
610                self.PatternTree.AppendItem(Id,text='HKL Plot Controls'),
611                rd.Controls)
612            self.PatternTree.SetItemPyData(
613                self.PatternTree.AppendItem(Id,text='Reflection List'),[])  #dummy entry for GUI use
614            self.PatternTree.SelectItem(Id)
615            self.PatternTree.Expand(Id)
616            self.Sngl = Id
617        return # success
618
619    def _Add_ImportMenu_powder(self,parent):
620        '''configure the Powder Data menus accord to the readers found in _init_Imports
621        '''
622        submenu = wx.Menu()
623        item = parent.AppendMenu(wx.ID_ANY, 'Powder Data',
624            submenu, help='Import Powder data')
625        for reader in self.ImportPowderReaderlist:
626            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
627                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
628            self.ImportMenuId[item.GetId()] = reader
629            self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
630        item = submenu.Append(wx.ID_ANY,
631            help='Import powder data, use file to try to determine format',
632            kind=wx.ITEM_NORMAL,text='guess format from file')
633        self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
634        submenu.AppendSeparator()
635        item = submenu.Append(wx.ID_ANY,
636            help='Create a powder data set entry that will be simulated',
637            kind=wx.ITEM_NORMAL,text='Simulate a dataset')
638        self.Bind(wx.EVT_MENU, self.OnDummyPowder, id=item.GetId())
639           
640    def ReadPowderInstprm(self,instfile):       #fix the write routine for [inst1,inst2] style
641        '''Read a GSAS-II (new) instrument parameter file
642
643        :param str instfile: name of instrument parameter file
644
645        '''
646        if os.path.splitext(instfile)[1].lower() != '.instprm': # invalid file
647            return None           
648        if not os.path.exists(instfile): # no such file
649            return None
650        File = open(instfile,'r')
651        S = File.readline()
652        if not S.startswith('#GSAS-II'): # not a valid file
653            File.close()
654            return None
655        newItems = []
656        newVals = []
657        while S:
658            if S[0] == '#':
659                S = File.readline()
660                continue
661            [item,val] = S[:-1].split(':')
662            newItems.append(item)
663            try:
664                newVals.append(float(val))
665            except ValueError:
666                newVals.append(val)                       
667            S = File.readline()               
668        File.close()
669        # add a second MT dict here. TOF parms? (BHT)
670        return G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}
671       
672    def ReadPowderIparm(self,instfile,bank,databanks,rd):
673        '''Read a GSAS (old) instrument parameter file
674
675        :param str instfile: name of instrument parameter file
676
677        :param int bank: the bank number read in the raw data file
678
679        :param int databanks: the number of banks in the raw data file.
680          If the number of banks in the data and instrument parameter files
681          agree, then the sets of banks are assumed to match up and bank
682          is used to select the instrument parameter file. If not, the user
683          is asked to make a selection.
684
685        :param obj rd: the raw data (histogram) data object. This
686          sets rd.instbank.
687
688        '''
689        if not os.path.exists(instfile): # no such file
690            return {}
691        fp = 0
692        try:
693            fp = open(instfile,'Ur')
694            Iparm = {}
695            for S in fp:
696                Iparm[S[:12]] = S[12:-1]
697        except IOError:
698            print('Error reading file:'+str(instfile))
699        if fp:       
700            fp.close()
701
702        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
703        hType = Iparm['INS   HTYPE '].strip()
704        if ibanks == 1: # there is only one bank here, return it
705            rd.instbank = 1
706            return Iparm
707        if 'PNT' in hType:
708            rd.instbank = bank
709        elif ibanks != databanks:
710            # number of banks in data and prm file not not agree, need a
711            # choice from a human here
712            choices = []
713            for i in range(1,1+ibanks):
714                choices.append('Bank '+str(i))
715            bank = rd.BlockSelector(
716                choices, self,
717                title='Select an instrument parameter bank for '+
718                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
719                '\nOr use Cancel to select from the default parameter sets',
720                header='Block Selector')
721        if bank is None: return {}
722        # pull out requested bank # bank from the data, and change the bank to 1
723        IparmS = {}
724        for key in Iparm:
725            if key[4:6] == "  ":
726                IparmS[key] = Iparm[key]
727            elif int(key[4:6].strip()) == bank:
728                IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
729        rd.instbank = bank
730        return IparmS
731                       
732    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
733        '''Open and read an instrument parameter file for a data file
734        Returns the list of parameters used in the data tree
735
736        :param obj rd: the raw data (histogram) data object.
737
738        :param str prevIparm: not used
739
740        :param str lastIparmfile: Name of last instrument parameter
741          file that was read, or a empty string.
742
743        :param str lastdatafile: Name of last data file that was read.
744
745        :returns: a list of two dicts, the first containing instrument parameters
746          and the second used for future TOF datasets (timemaps?)
747
748        '''
749        def SetPowderInstParms(Iparm, rd):
750            '''extracts values from instrument parameters in rd.instdict
751            or in array Iparm.
752            Create and return the contents of the instrument parameter tree entry.
753            '''
754            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
755            # override inst values with values read from data file
756            if rd.instdict.get('type'):
757                DataType = rd.instdict.get('type')
758            data = [DataType,]
759            instname = Iparm.get('INS  1INAME ')
760            if instname:
761                rd.Sample['InstrName'] = instname.strip()
762            if 'C' in DataType:
763                wave1 = None
764                wave2 = 0.0
765                if rd.instdict.get('wave'):
766                    wl = rd.instdict.get('wave')
767                    wave1 = wl[0]
768                    if len(wl) > 1: wave2 = wl[1]
769                s = Iparm['INS  1 ICONS']
770                if not wave1:
771                    wave1 = G2IO.sfloat(s[:10])
772                    wave2 = G2IO.sfloat(s[10:20])
773                v = (wave1,wave2,
774                     G2IO.sfloat(s[20:30]),G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
775                if not v[1]:
776                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
777                    v = (v[0],v[2],v[4])
778                    codes = [0,0,0,0]
779                else:
780                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
781                    codes = [0,0,0,0,0,0]
782                data.extend(v)
783                v1 = Iparm['INS  1PRCF1 '].split()                                                 
784                v = Iparm['INS  1PRCF11'].split()
785                data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
786                azm = float(Iparm.get('INS  1DETAZM','0.0'))
787                v = Iparm['INS  1PRCF12'].split()
788                if v1[0] == 3:
789                    data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
790                else:
791                    data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
792                codes.extend([0,0,0,0,0,0,0])
793                return [G2IO.makeInstDict(names,data,codes),{}]
794            elif 'T' in DataType:
795                names = ['Type','2-theta','difC','difA','Zero','alpha','beta-0','beta-1',
796                    'beta-q','sig-0','sig-1','sig-q','X','Y','Azimuth']
797                codes = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
798                azm = 0.
799                if 'INS  1DETAZM' in Iparm:
800                    azm = float(Iparm['INS  1DETAZM'])
801                s = Iparm['INS  1BNKPAR'].split()
802                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
803                s = Iparm['INS  1 ICONS'].split()
804                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])    #difC, difA, Zero
805                s = Iparm['INS  1PRCF1 '].split()
806                pfType = int(s[0])
807                s = Iparm['INS  1PRCF11'].split()
808                if abs(pfType) == 1:
809                    data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])])
810                    s = Iparm['INS  1PRCF12'].split()
811                    data.extend([0.0,0.0,G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])
812                elif abs(pfType) in [3,4,5]:
813                    data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])
814                    if abs(pfType) == 4:
815                        data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,azm])
816                    else:
817                        s = Iparm['INS  1PRCF12'].split()
818                        data.extend([0.0,0.0,G2IO.sfloat(s[0]),0.0,0.0,0.0,azm])                       
819                Inst1 = G2IO.makeInstDict(names,data,codes)
820                Inst2 = {}
821                if pfType < 0:
822                    Ipab = 'INS  1PAB'+str(-pfType)
823                    Npab = int(Iparm[Ipab+'  '].strip())
824                    Inst2['Pdabc'] = []
825                    for i in range(Npab):
826                        k = Ipab+str(i+1).rjust(2)
827                        s = Iparm[k].split()
828                        Inst2['Pdabc'].append([float(t) for t in s])
829                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
830                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
831                if 'INS  1I ITYP' in Iparm:
832                    s = Iparm['INS  1I ITYP'].split()
833                    Ityp = int(s[0])
834                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
835                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
836                    if Ityp in [1,2,4]:
837                        Inst2['Itype'] = Itypes[Ityp-1]
838                        Inst2['Tminmax'] = Tminmax
839                        Icoeff = []
840                        Iesd = []
841                        Icovar = []                   
842                        for i in range(3):
843                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
844                            Icoeff += [float(S) for S in s]
845                            s = Iparm['INS  1IECOF'+str(i+1)].split()
846                            Iesd += [float(S) for S in s]
847                        for i in range(8):
848                            s = Iparm['INS  1IECOR'+str(i+1)].split()
849                            Icovar += [float(S) for S in s]
850                        Inst2['Icoeff'] = Icoeff
851                        Inst2['Iesd'] = Iesd
852                        Inst2['Icovar'] = Icovar
853                return [Inst1,Inst2]
854
855        # stuff we might need from the reader
856        filename = rd.powderentry[0]
857        bank = rd.powderentry[2]
858        numbanks = rd.numbanks
859        # is there an instrument parameter file defined for the current data set?
860        if rd.instparm or (lastdatafile == filename and lastIparmfile):
861            if rd.instparm:
862                instfile = os.path.join(os.path.split(filename)[0],
863                                    rd.instparm)
864            else:
865                # for multiple reads of one data file, reuse the inst parm file
866                instfile = lastIparmfile
867            if os.path.exists(instfile):
868                #print 'debug: try read',instfile
869                instParmList = self.ReadPowderInstprm(instfile)
870                if instParmList is not None:
871                    rd.instfile = instfile
872                    rd.instmsg = 'GSAS-II file '+instfile
873                    return instParmList
874                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
875                if Iparm:
876                    #print 'debug: success'
877                    rd.instfile = instfile
878                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
879                    return SetPowderInstParms(Iparm,rd)
880            else:
881                self.ErrorDialog('Open Error','Error opening instrument parameter file '
882                    +str(instfile)+' requested by file '+ filename)
883        # is there an instrument parameter file matching the current file
884        # with extension .inst or .prm? If so read it
885        basename = os.path.splitext(filename)[0]
886        for ext in '.instprm','.prm','.inst','.ins':
887            instfile = basename + ext
888            instParmList = self.ReadPowderInstprm(instfile)
889            if instParmList is not None:
890                rd.instfile = instfile
891                rd.instmsg = 'GSAS-II file '+instfile
892                return instParmList
893            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
894            if Iparm:
895                #print 'debug: success'
896                rd.instfile = instfile
897                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
898                return SetPowderInstParms(Iparm,rd)
899            else:
900                #print 'debug: open/read failed',instfile
901                pass # fail silently
902
903        # did we read the data file from a zip? If so, look there for a
904        # instrument parameter file
905        if self.zipfile:
906            for ext in '.instprm','.prm','.inst','.ins':
907                instfile = G2IO.ExtractFileFromZip(
908                    self.zipfile,
909                    selection=os.path.split(basename + ext)[1],
910                    parent=self)
911                if instfile is not None and instfile != self.zipfile:
912                    print 'debug:',instfile,'created from ',self.zipfile
913                    instParmList = self.ReadPowderInstprm(instfile)
914                    if instParmList is not None:
915                        rd.instfile = instfile
916                        rd.instmsg = 'GSAS-II file '+instfile
917                        return instParmList
918                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
919                    if Iparm:
920                        rd.instfile = instfile
921                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
922                        return SetPowderInstParms(Iparm,rd)
923                    else:
924                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
925                        pass # fail silently
926
927        while True: # loop until we get a file that works or we get a cancel
928            instfile = ''
929            dlg = wx.FileDialog(
930                self,
931                'Choose inst. param file for "'
932                +rd.idstring
933                +'" (or Cancel for default)',
934                '.', '',
935                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
936                'GSAS-II iparm file (*.instprm)|*.instprm|'
937                'All files (*.*)|*.*', 
938                wx.OPEN|wx.CHANGE_DIR)
939            if os.path.exists(lastIparmfile):
940                dlg.SetFilename(lastIparmfile)
941            if dlg.ShowModal() == wx.ID_OK:
942                instfile = dlg.GetPath()
943            dlg.Destroy()
944            if not instfile: break
945            instParmList = self.ReadPowderInstprm(instfile)
946            if instParmList is not None:
947                rd.instfile = instfile
948                rd.instmsg = 'GSAS-II file '+instfile
949                return instParmList
950            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
951            if Iparm:
952                #print 'debug: success with',instfile
953                rd.instfile = instfile
954                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
955                return SetPowderInstParms(Iparm,rd)
956            else:
957                self.ErrorDialog('Read Error',
958                                 'Error opening/reading file '+str(instfile))
959       
960        # still no success: offer user choice of defaults
961        while True: # loop until we get a choice
962            choices = []
963            head = 'Select from default instrument parameters for '+rd.idstring
964
965            for l in rd.defaultIparm_lbl:
966                choices.append('Defaults for '+l)
967            res = rd.BlockSelector(
968                choices,
969                ParentFrame=self,
970                title=head,
971                header='Select default inst parms',
972                useCancel=False)
973            if res is None: continue
974            rd.instfile = ''
975            rd.instmsg = 'default: '+rd.defaultIparm_lbl[res]
976            return SetPowderInstParms(rd.defaultIparms[res],rd)
977
978    def OnImportPowder(self,event):
979        '''Called in response to an Import/Powder Data/... menu item
980        to read a powder diffraction data set.
981        dict self.ImportMenuId is used to look up the specific
982        reader item associated with the menu item, which will be
983        None for the last menu item, which is the "guess" option
984        where all appropriate formats will be tried.
985
986        Also reads an instrument parameter file for each dataset.
987        '''
988        reqrdr = self.ImportMenuId.get(event.GetId())  # look up which format was requested
989        rdlist = self.OnImportGeneric(
990            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
991        if len(rdlist) == 0: return
992        self.CheckNotebook()
993        Iparm = None
994        lastIparmfile = ''
995        lastdatafile = ''
996        for rd in rdlist:
997            # get instrument parameters for each dataset
998            Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
999            if rd.repeat_instparm: 
1000                lastIparmfile = rd.instfile
1001            lastdatafile = rd.powderentry[0]
1002            print 'Read powder data '+str(rd.idstring)+ \
1003                ' from file '+str(self.lastimport) + \
1004                ' with parameters from '+str(rd.instmsg)
1005            # data are read, now store them in the tree
1006            Id = self.PatternTree.AppendItem(parent=self.root,
1007                text='PWDR '+rd.idstring)
1008            if 'T' in Iparm1['Type'][0]:
1009                if not rd.clockWd and rd.GSAS:
1010                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1011                cw = np.diff(rd.powderdata[0])
1012                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1013                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1014                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1015                if 'Itype' in Iparm2:
1016                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1017                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1018                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1019                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1020                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1021                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1022                    var += WYI*rd.powderdata[1]**2
1023                    var /= YI**2
1024                    rd.powderdata[2] = 1./var
1025                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1026                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1027                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1028            Tmin = min(rd.powderdata[0])
1029            Tmax = max(rd.powderdata[0])
1030            valuesdict = {
1031                'wtFactor':1.0,
1032                'Dummy':False,
1033                'ranId':ran.randint(0,sys.maxint),
1034                }
1035            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1036            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1037            self.PatternTree.SetItemPyData(
1038                self.PatternTree.AppendItem(Id,text='Comments'),
1039                rd.comments)
1040            self.PatternTree.SetItemPyData(
1041                self.PatternTree.AppendItem(Id,text='Limits'),
1042                [(Tmin,Tmax),[Tmin,Tmax]])
1043            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1044            self.PatternTree.SetItemPyData(
1045                self.PatternTree.AppendItem(Id,text='Background'),
1046                [['chebyschev',True,3,1.0,0.0,0.0],
1047                 {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1048            self.PatternTree.SetItemPyData(
1049                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1050                [Iparm1,Iparm2])
1051            self.PatternTree.SetItemPyData(
1052                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1053                rd.Sample)
1054            self.PatternTree.SetItemPyData(
1055                self.PatternTree.AppendItem(Id,text='Peak List')
1056                ,[])
1057            self.PatternTree.SetItemPyData(
1058                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1059                [])
1060            self.PatternTree.SetItemPyData(
1061                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1062                [])
1063            self.PatternTree.SetItemPyData(
1064                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1065                {})
1066            self.PatternTree.Expand(Id)
1067        self.PatternTree.SelectItem(Id)
1068        return # success
1069
1070    def OnDummyPowder(self,event):
1071        '''Called in response to Import/Powder Data/Simulate menu item
1072        to create a Dummy powder diffraction data set.
1073
1074        Reads an instrument parameter file and then gets input from the user
1075        '''
1076        rd = G2IO.ImportPowderData(
1077            extensionlist=tuple(),
1078            strictExtension=False,
1079            formatName = 'Simulate dataset',
1080            longFormatName = 'Compute a simulated pattern')
1081        rd.powderentry[0] = '' # no filename
1082        # #self.powderentry[1] = pos # bank offset (N/A here)
1083        rd.powderentry[2] = 1 # only one bank
1084        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1085        self.CheckNotebook()
1086        Iparm = None
1087        lastIparmfile = ''
1088        lastdatafile = ''
1089        self.zipfile = None
1090        # get instrument parameters for
1091        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1092        if 'T' in Iparm1['Type'][0]:
1093            print('TOF simulation not supported yet')
1094            return False
1095        else:
1096            # need to get name, 2theta start, end, step
1097            rd.idstring = ' CW'
1098            if 'X' in Iparm1['Type'][0]:
1099                rd.idstring = 'CW x-ray simulation'
1100            else:
1101                rd.idstring = 'CW neutron simulation'
1102            # base initial range on wavelength
1103            wave = Iparm1.get('Lam')
1104            if wave:
1105                wave = wave[0]
1106            else:
1107                wave = Iparm1.get('Lam1')
1108                if wave:
1109                    wave = wave[0]
1110        N = 0
1111        while (N < 3): # insist on a dataset with a few points
1112            names = ('dataset name', 'start angle', 'end angle', 'step size')
1113            if not wave or wave < 1.0:
1114                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1115            else:
1116                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1117            dlg = G2gd.ScrolledMultiEditor(
1118                self,[inp] * len(inp),range(len(inp)),names,
1119                header='Enter simulation name and range',
1120                minvals=(None,0.001,0.001,0.0001),
1121                maxvals=(None,180.,180.,.1),
1122                sizevals=((225,-1),)
1123                )
1124            dlg.CenterOnParent()
1125            if dlg.ShowModal() == wx.ID_OK:
1126                if inp[1] > inp[2]:
1127                    end,start,step = inp[1:]
1128                else:               
1129                    start,end,step = inp[1:]
1130                step = abs(step)
1131            else:
1132                return False
1133            N = int((end-start)/step)+1
1134            x = np.linspace(start,end,N,True)
1135            N = len(x)
1136        rd.powderdata = [
1137            np.array(x), # x-axis values
1138            np.zeros_like(x), # powder pattern intensities
1139            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1140            np.zeros_like(x), # calc. intensities (zero)
1141            np.zeros_like(x), # calc. background (zero)
1142            np.zeros_like(x), # obs-calc profiles
1143            ]
1144        Tmin = rd.powderdata[0][0]
1145        Tmax = rd.powderdata[0][-1]
1146        # data are read, now store them in the tree
1147        Id = self.PatternTree.AppendItem(parent=self.root,
1148                                         text='PWDR '+inp[0])
1149        valuesdict = {
1150            'wtFactor':1.0,
1151            'Dummy':True,
1152            'ranId':ran.randint(0,sys.maxint),
1153            }
1154        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1155        self.PatternTree.SetItemPyData(
1156            self.PatternTree.AppendItem(Id,text='Comments'),
1157            rd.comments)
1158        self.PatternTree.SetItemPyData(
1159            self.PatternTree.AppendItem(Id,text='Limits'),
1160            [(Tmin,Tmax),[Tmin,Tmax]])
1161        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1162        self.PatternTree.SetItemPyData(
1163            self.PatternTree.AppendItem(Id,text='Background'),
1164            [['chebyschev',True,3,1.0,0.0,0.0],
1165             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1166        self.PatternTree.SetItemPyData(
1167            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1168            [Iparm1,Iparm2])
1169        self.PatternTree.SetItemPyData(
1170            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1171            rd.Sample)
1172        self.PatternTree.SetItemPyData(
1173            self.PatternTree.AppendItem(Id,text='Peak List')
1174            ,[])
1175        self.PatternTree.SetItemPyData(
1176            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1177            [])
1178        self.PatternTree.SetItemPyData(
1179            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1180            [])
1181        self.PatternTree.SetItemPyData(
1182            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1183            {})
1184        self.PatternTree.Expand(Id)
1185        self.PatternTree.SelectItem(Id)
1186        print('Added simulation powder data '+str(rd.idstring)+ 
1187              ' with parameters from '+str(rd.instmsg))
1188        return # success
1189
1190    def _init_Exports(self,menu):
1191        '''Find exporter routines and add them into menus
1192        '''
1193        # set up the top-level menus
1194        projectmenu = wx.Menu()
1195        item = menu.AppendMenu(
1196            wx.ID_ANY, 'Entire project as',
1197            projectmenu, help='Export entire project')
1198
1199        phasemenu = wx.Menu()
1200        item = menu.AppendMenu(
1201            wx.ID_ANY, 'Phase as',
1202            phasemenu, help='Export phase or sometimes phases')
1203
1204        powdermenu = wx.Menu()
1205        item = menu.AppendMenu(
1206            wx.ID_ANY, 'Powder data as',
1207            powdermenu, help='Export powder diffraction histogram(s)')
1208
1209        singlemenu = wx.Menu()
1210        item = menu.AppendMenu(
1211            wx.ID_ANY, 'Single crystal data as',
1212            singlemenu, help='Export single crystal histogram(s)')
1213
1214        imagemenu = wx.Menu()
1215        item = menu.AppendMenu(
1216            wx.ID_ANY, 'Images as',
1217            imagemenu, help='Export powder image(s)')
1218
1219        mapmenu = wx.Menu()
1220        item = menu.AppendMenu(
1221            wx.ID_ANY, 'Maps as',
1222            mapmenu, help='Export density map(s)')
1223
1224        # pdfmenu = wx.Menu()
1225        # item = menu.AppendMenu(
1226        #     wx.ID_ANY, 'PDFs as',
1227        #     pdfmenu, help='Export pair distribution function(s)')
1228
1229        # find all the exporter files
1230        pathlist = sys.path
1231        filelist = []
1232        for path in pathlist:
1233            for filename in glob.iglob(os.path.join(
1234                path,
1235                "G2export*.py")):
1236                filelist.append(filename)   
1237        filelist = sorted(list(set(filelist))) # remove duplicates
1238        exporterlist = []
1239        # go through the routines and import them, saving objects that
1240        # have export routines (method Exporter)
1241        for filename in filelist:
1242            path,rootname = os.path.split(filename)
1243            pkg = os.path.splitext(rootname)[0]
1244            try:
1245                fp = None
1246                fp, fppath,desc = imp.find_module(pkg,[path,])
1247                pkg = imp.load_module(pkg,fp,fppath,desc)
1248                for clss in inspect.getmembers(pkg): # find classes defined in package
1249                    if clss[0].startswith('_'): continue
1250                    if inspect.isclass(clss[1]):
1251                        # check if we have the required methods
1252                        for m in 'Exporter','loadParmDict':
1253                            if not hasattr(clss[1],m): break
1254                            if not callable(getattr(clss[1],m)): break
1255                        else:
1256                            exporter = clss[1](self) # create an export instance
1257                            exporterlist.append(exporter)
1258            except AttributeError:
1259                print 'Import_'+errprefix+': Attribute Error'+str(filename)
1260                pass
1261            except ImportError:
1262                print 'Import_'+errprefix+': Error importing file'+str(filename)
1263                pass
1264            if fp: fp.close()
1265        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
1266        for obj in exporterlist:
1267            #print 'exporter',obj
1268            for typ in obj.exporttype:
1269                if typ == "project":
1270                    submenu = projectmenu
1271                elif typ == "phase":
1272                    submenu = phasemenu
1273                elif typ == "powder":
1274                    submenu = powdermenu
1275                elif typ == "single":
1276                    submenu = singlemenu
1277                elif typ == "image":
1278                    submenu = imagemenu
1279                elif typ == "map":
1280                    submenu = mapmenu
1281                # elif typ == "pdf":
1282                #     submenu = pdfmenu
1283                else:
1284                    print("Error, unknown type in "+str(obj))
1285                    break
1286                item = submenu.Append(
1287                    wx.ID_ANY,
1288                    help=obj.longFormatName,
1289                    kind=wx.ITEM_NORMAL,
1290                    text=obj.formatName)
1291                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
1292                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
1293        #code to debug an Exporter. much is hard-coded below, but code is reloaded before
1294        # each use allowing faster development
1295        # def DebugExport(event):
1296        #     print 'start reload'
1297        #     reload(G2IO)
1298        #     import G2export_ASCIImap as dev
1299        #     reload(dev)
1300        #     dev.ExportMapASCII(self).Exporter(event)
1301        # item = menu.Append(
1302        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1303        #     help="debug exporter",text="test Export")
1304        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
1305        # #self.ExportLookup[item.GetId()] = 'image'
1306        # self.ExportLookup[item.GetId()] = 'map'
1307
1308    def _Add_ExportMenuItems(self,parent):
1309        item = parent.Append(
1310            help='Select PWDR item to enable',id=wx.ID_ANY,
1311            kind=wx.ITEM_NORMAL,
1312            text='Export Powder Patterns...')
1313        self.ExportPattern.append(item)
1314        item.Enable(False)
1315        self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1316
1317        item = parent.Append(
1318            help='',id=wx.ID_ANY,
1319            kind=wx.ITEM_NORMAL,
1320            text='Export All Peak Lists...')
1321        self.ExportPeakList.append(item)
1322        item.Enable(True)
1323        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1324
1325        item = parent.Append(
1326            help='',id=wx.ID_ANY,
1327            kind=wx.ITEM_NORMAL,
1328            text='Export HKLs...')
1329        self.ExportHKL.append(item)
1330        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1331
1332        item = parent.Append(
1333            help='Select PDF item to enable',
1334            id=wx.ID_ANY,
1335            kind=wx.ITEM_NORMAL,
1336            text='Export PDF...')
1337        self.ExportPDF.append(item)
1338        item.Enable(False)
1339        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1340
1341    def FillMainMenu(self,menubar):
1342        '''Define contents of the main GSAS-II menu for the (main) data tree window
1343        in the mac, used also for the data item windows as well.
1344        '''
1345        File = wx.Menu(title='')
1346        menubar.Append(menu=File, title='&File')
1347        self._Add_FileMenuItems(File)
1348        Data = wx.Menu(title='')
1349        menubar.Append(menu=Data, title='Data')
1350        self._Add_DataMenuItems(Data)
1351        Calculate = wx.Menu(title='')       
1352        menubar.Append(menu=Calculate, title='&Calculate')
1353        self._Add_CalculateMenuItems(Calculate)
1354        Import = wx.Menu(title='')       
1355        menubar.Append(menu=Import, title='Import')
1356        self._Add_ImportMenu_Phase(Import)
1357        self._Add_ImportMenu_powder(Import)
1358        self._Add_ImportMenu_Sfact(Import)
1359        #======================================================================
1360        # Code to help develop/debug an importer, much is hard-coded below
1361        # but module is reloaded before each use, allowing faster testing
1362        # def DebugImport(event):
1363        #     print 'start reload'
1364        #     import G2phase_ISO as dev
1365        #     reload(dev)
1366        #     rd = dev.ISODISTORTPhaseReader()
1367        #     self.ImportMenuId[event.GetId()] = rd
1368        #     self.OnImportPhase(event)
1369            # or ----------------------------------------------------------------------
1370            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
1371            # special debug code
1372            # or ----------------------------------------------------------------------
1373            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
1374            # fp = open(filename,'Ur')
1375            # if not rd.ContentsValidator(fp):
1376            #     print 'not validated'
1377            #     # make a list of used phase ranId's
1378            # phaseRIdList = []
1379            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1380            # if sub:
1381            #     item, cookie = self.PatternTree.GetFirstChild(sub)
1382            #     while item:
1383            #         phaseName = self.PatternTree.GetItemText(item)
1384            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
1385            #         if ranId: phaseRIdList.append(ranId)
1386            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1387            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
1388            #     print 'read OK'
1389        # item = Import.Append(
1390        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1391        #     help="debug importer",text="test importer")
1392        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
1393        #======================================================================
1394        self.ExportMenu = wx.Menu(title='')
1395        menubar.Append(menu=self.ExportMenu, title='Export')
1396        self._init_Exports(self.ExportMenu)
1397        self._Add_ExportMenuItems(self.ExportMenu)
1398        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1399            morehelpitems=[('&Tutorials','Tutorials')])
1400        menubar.Append(menu=HelpMenu,title='&Help')
1401
1402    def _init_ctrls(self, parent):
1403        wx.Frame.__init__(self, name='GSASII', parent=parent,
1404            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1405        clientSize = wx.ClientDisplayRect()
1406        Size = self.GetSize()
1407        xPos = clientSize[2]-Size[0]
1408        self.SetPosition(wx.Point(xPos,clientSize[1]))
1409        self._init_Imports()
1410        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1411        self.MakePDF = []
1412        self.Refine = []
1413        self.SeqRefine = []
1414        self.ExportPattern = []
1415        self.ExportPeakList = []
1416        self.ExportHKL = []
1417        self.ExportPDF = []
1418        self.ExportPhase = []
1419        self.ExportCIF = []
1420        #
1421        self.GSASIIMenu = wx.MenuBar()
1422        self.FillMainMenu(self.GSASIIMenu)
1423        self.SetMenuBar(self.GSASIIMenu)
1424        self.Bind(wx.EVT_SIZE, self.OnSize)
1425        self.CreateStatusBar()
1426        self.mainPanel = wx.Panel(self,-1)
1427       
1428        wxID_PATTERNTREE = wx.NewId()
1429        self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE,
1430            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1431        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,
1432            self.OnPatternTreeSelChanged, id=wxID_PATTERNTREE)
1433        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1434            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1435        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1436            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1437        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1438            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1439        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1440            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1441        self.root = self.PatternTree.AddRoot('Loaded Data: ')
1442       
1443        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1444            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1445        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1446        plotFrame.Show()
1447       
1448        self.dataDisplay = None
1449       
1450    def __init__(self, parent):
1451        self.ExportLookup = {}
1452        self._init_ctrls(parent)
1453        self.Image = wx.Image(
1454            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
1455            wx.BITMAP_TYPE_ICO)
1456        if "wxMSW" in wx.PlatformInfo:
1457            img = self.Image.Scale(16, 16).ConvertToBitmap()
1458        elif "wxGTK" in wx.PlatformInfo:
1459            img = self.Image.Scale(22, 22).ConvertToBitmap()
1460        else:
1461            img = self.Image.ConvertToBitmap()
1462        self.SetIcon(wx.IconFromBitmap(img))
1463        self.Bind(wx.EVT_CLOSE, self.ExitMain)
1464        # various defaults
1465        self.oldFocus = None
1466        self.GSASprojectfile = ''
1467        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
1468        self.undofile = ''
1469        self.TreeItemDelete = False
1470        self.Offset = [0.0,0.0]
1471        self.delOffset = .02
1472        self.refOffset = -100.0
1473        self.refDelt = .01
1474        self.Weight = False
1475        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
1476        self.IfPlot = False
1477        self.PatternId = 0
1478        self.PickId = 0
1479        self.PeakTable = []
1480        self.LimitsTable = []
1481        self.HKL = []
1482        self.Lines = []
1483        self.itemPicked = None
1484        self.dataFrame = None
1485        self.Interpolate = 'nearest'
1486        self.ContourColor = 'Paired'
1487        self.VcovColor = 'RdYlGn'
1488        self.RamaColor = 'Blues'
1489        self.Projection = 'equal area'
1490        self.logPlot = False
1491        self.qPlot = False
1492        self.Contour = False
1493        self.Legend = False
1494        self.SinglePlot = False
1495        self.SubBack = False
1496        self.plotView = 0
1497        self.Image = 0
1498        self.oldImagefile = ''
1499        self.ImageZ = []
1500        self.Integrate = 0
1501        self.imageDefault = {}
1502        self.Sngl = 0
1503        self.ifGetRing = False
1504        self.MaskKey = ''           #trigger for making image masks
1505        arg = sys.argv
1506        if len(arg) > 1:
1507            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1508            self.dirname = os.path.dirname(arg[1])
1509            if self.dirname: os.chdir(self.dirname)
1510            try:
1511                G2IO.ProjFileOpen(self)
1512                self.PatternTree.Expand(self.root)
1513                for item in self.Refine: item.Enable(True)
1514                for item in self.SeqRefine: item.Enable(True)
1515            except:
1516                print 'Error opening file',arg[1]
1517
1518    def OnSize(self,event):
1519        'Called when the main window is resized. Not sure why'
1520        w,h = self.GetClientSizeTuple()
1521        self.mainPanel.SetSize(wx.Size(w,h))
1522        self.PatternTree.SetSize(wx.Size(w,h))
1523                       
1524    def OnPatternTreeSelChanged(self, event):
1525        '''Called when a data tree item is selected'''
1526        if self.TreeItemDelete:
1527            self.TreeItemDelete = False
1528        else:
1529            pltNum = self.G2plotNB.nb.GetSelection()
1530            if pltNum >= 0:                         #to avoid the startup with no plot!
1531                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1532                pltPlot = pltPage.figure
1533            item = event.GetItem()
1534            G2gd.MovePatternTreeToGrid(self,item)
1535            if self.oldFocus:
1536                self.oldFocus.SetFocus()
1537       
1538    def OnPatternTreeItemCollapsed(self, event):
1539        'Called when a tree item is collapsed'
1540        event.Skip()
1541
1542    def OnPatternTreeItemExpanded(self, event):
1543        'Called when a tree item is expanded'
1544        event.Skip()
1545       
1546    def OnPatternTreeItemDelete(self, event):
1547        'Called when a tree item is deleted -- not sure what this does'
1548        self.TreeItemDelete = True
1549
1550    def OnPatternTreeItemActivated(self, event):
1551        'Called when a tree item is activated'
1552        event.Skip()
1553       
1554    def OnPatternTreeKeyDown(self,event):
1555        'Not sure what this does'
1556        key = event.GetKeyCode()
1557        item = self.PickId
1558        if type(item) is int: return # is this the toplevel in tree?
1559        if key == wx.WXK_UP:
1560            self.oldFocus = wx.Window.FindFocus()
1561            self.PatternTree.GetPrevSibling(item)
1562        elif key == wx.WXK_DOWN:
1563            self.oldFocus = wx.Window.FindFocus()
1564            self.PatternTree.GetNextSibling(item)
1565               
1566    def OnReadPowderPeaks(self,event):
1567        'Bound to menu Data/Read Powder Peaks -- still needed?'
1568        Cuka = 1.54052
1569        self.CheckNotebook()
1570        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
1571            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
1572        try:
1573            if dlg.ShowModal() == wx.ID_OK:
1574                self.HKL = []
1575                self.powderfile = dlg.GetPath()
1576                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
1577                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
1578                data = ['PKS',Cuka,0.0]
1579                names = ['Type','Lam','Zero'] 
1580                codes = [0,0,0]
1581                inst = [G2IO.makeInstDict(names,data,codes),{}]
1582                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1583                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
1584                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),peaks)
1585                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1586                self.PatternTree.Expand(Id)
1587                self.PatternTree.SelectItem(Id)
1588                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1589        finally:
1590            dlg.Destroy()
1591           
1592    def OnImageRead(self,event):
1593        'Called to read in an image in any known format'
1594        self.CheckNotebook()
1595        dlg = wx.FileDialog(
1596            self, 'Choose image files', '.', '',
1597            'Any image file (*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img)|'
1598            '*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.zip|'
1599            'European detector file (*.edf)|*.edf|'
1600            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
1601            'MAR file (*.mar*)|*.mar*|'
1602            'GE Image (*.avg;*.sum)|*.avg;*.sum|'
1603            'ADSC Image (*.img)|*.img|'
1604            'GSAS-II Image (*.G2img)|*.G2img|'
1605            'Zip archive (*.zip)|*.zip|'
1606            'All files (*.*)|*.*',
1607            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
1608        try:
1609            if dlg.ShowModal() == wx.ID_OK:
1610                imagefiles = dlg.GetPaths()
1611                imagefiles.sort()
1612                for imagefile in imagefiles:
1613                    # if a zip file, open and extract
1614                    if os.path.splitext(imagefile)[1].lower() == '.zip':
1615                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
1616                        if extractedfile is not None and extractedfile != imagefile:
1617                            imagefile = extractedfile
1618                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
1619                    if Comments:
1620                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
1621                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1622                        Imax = np.amax(Image)
1623                        Imin = max(0.0,np.amin(Image))          #force positive
1624                        if self.imageDefault:
1625                            Data = copy.copy(self.imageDefault)
1626                            Data['showLines'] = True
1627                            Data['ring'] = []
1628                            Data['rings'] = []
1629                            Data['cutoff'] = 10
1630                            Data['pixLimit'] = 20
1631                            Data['edgemin'] = 100000000
1632                            Data['calibdmin'] = 0.5
1633                            Data['calibskip'] = 0
1634                            Data['ellipses'] = []
1635                            Data['calibrant'] = ''
1636                            Data['GonioAngles'] = [0.,0.,0.]
1637                            Data['DetDepth'] = 0.
1638                            Data['DetDepthRef'] = False
1639                        else:
1640                            Data['type'] = 'PWDR'
1641                            Data['color'] = 'Paired'
1642                            Data['tilt'] = 0.0
1643                            Data['rotation'] = 0.0
1644                            Data['showLines'] = False
1645                            Data['ring'] = []
1646                            Data['rings'] = []
1647                            Data['cutoff'] = 10
1648                            Data['pixLimit'] = 20
1649                            Data['calibdmin'] = 0.5
1650                            Data['calibskip'] = 0
1651                            Data['edgemin'] = 100000000
1652                            Data['ellipses'] = []
1653                            Data['GonioAngles'] = [0.,0.,0.]
1654                            Data['DetDepth'] = 0.
1655                            Data['DetDepthRef'] = False
1656                            Data['calibrant'] = ''
1657                            Data['IOtth'] = [2.0,5.0]
1658                            Data['LRazimuth'] = [135,225]
1659                            Data['azmthOff'] = 0.0
1660                            Data['outChannels'] = 2500
1661                            Data['outAzimuths'] = 1
1662                            Data['centerAzm'] = False
1663                            Data['fullIntegrate'] = False
1664                            Data['setRings'] = False
1665                            Data['background image'] = ['',1.0]                           
1666                        Data['setDefault'] = False
1667                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1668                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
1669                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1670                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1671                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
1672                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'strain':np.zeros((3,3))})
1673                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
1674                        self.PickId = Id
1675                        self.Image = Id
1676                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1677                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
1678        finally:
1679            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
1680            os.chdir(path)
1681            dlg.Destroy()
1682
1683    def CheckNotebook(self):
1684        '''Make sure the data tree has the minimally expected controls.
1685        (BHT) correct?
1686        '''
1687        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
1688            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
1689            self.PatternTree.SetItemPyData(sub,[''])
1690        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
1691            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
1692            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
1693        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
1694            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
1695            self.PatternTree.SetItemPyData(sub,{})
1696        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
1697            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
1698            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
1699        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
1700            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
1701            self.PatternTree.SetItemPyData(sub,{})
1702        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
1703            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
1704            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
1705                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
1706               
1707    class CopyDialog(wx.Dialog):
1708        '''Creates a dialog for copying control settings between
1709        data tree items'''
1710        def __init__(self,parent,title,text,data):
1711            wx.Dialog.__init__(self,parent,-1,title, 
1712                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1713            self.data = data
1714            panel = wx.Panel(self)
1715            mainSizer = wx.BoxSizer(wx.VERTICAL)
1716            topLabl = wx.StaticText(panel,-1,text)
1717            mainSizer.Add((10,10),1)
1718            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1719            mainSizer.Add((10,10),1)
1720            ncols = len(data)/40+1
1721            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=ncols,hgap=2,vgap=2)
1722            for id,item in enumerate(self.data):
1723                ckbox = wx.CheckBox(panel,id,item[1])
1724                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
1725                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
1726            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1727            OkBtn = wx.Button(panel,-1,"Ok")
1728            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1729            cancelBtn = wx.Button(panel,-1,"Cancel")
1730            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1731            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1732            btnSizer.Add((20,20),1)
1733            btnSizer.Add(OkBtn)
1734            btnSizer.Add((20,20),1)
1735            btnSizer.Add(cancelBtn)
1736            btnSizer.Add((20,20),1)
1737           
1738            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1739            panel.SetSizer(mainSizer)
1740            panel.Fit()
1741            self.Fit()
1742       
1743        def OnCopyChange(self,event):
1744            id = event.GetId()
1745            self.data[id][0] = self.FindWindowById(id).GetValue()       
1746           
1747        def OnOk(self,event):
1748            parent = self.GetParent()
1749            parent.Raise()
1750            self.EndModal(wx.ID_OK)             
1751           
1752        def OnCancel(self,event):
1753            parent = self.GetParent()
1754            parent.Raise()
1755            self.EndModal(wx.ID_CANCEL)             
1756           
1757        def GetData(self):
1758            return self.data
1759       
1760    class SumDialog(wx.Dialog):
1761        'Allows user to supply scale factor(s) when summing data'
1762        def __init__(self,parent,title,text,dataType,data):
1763            wx.Dialog.__init__(self,parent,-1,title, 
1764                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1765            self.data = data
1766            panel = wx.Panel(self)
1767            mainSizer = wx.BoxSizer(wx.VERTICAL)
1768            topLabl = wx.StaticText(panel,-1,text)
1769            mainSizer.Add((10,10),1)
1770            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1771            mainSizer.Add((10,10),1)
1772            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1773            for id,item in enumerate(self.data[:-1]):
1774                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
1775                name.SetEditable(False)
1776                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1777                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1778                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1779                dataGridSizer.Add(scale,0,wx.LEFT,10)
1780                dataGridSizer.Add(name,0,wx.RIGHT,10)
1781            if dataType:
1782                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
1783                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
1784                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
1785                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
1786                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
1787                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
1788            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1789            OkBtn = wx.Button(panel,-1,"Ok")
1790            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1791            cancelBtn = wx.Button(panel,-1,"Cancel")
1792            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1793            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1794            btnSizer.Add((20,20),1)
1795            btnSizer.Add(OkBtn)
1796            btnSizer.Add((20,20),1)
1797            btnSizer.Add(cancelBtn)
1798            btnSizer.Add((20,20),1)
1799           
1800            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1801            panel.SetSizer(mainSizer)
1802            panel.Fit()
1803            self.Fit()
1804
1805        def OnScaleChange(self,event):
1806            id = event.GetId()
1807            value = self.FindWindowById(id).GetValue()
1808            try:
1809                self.data[id][0] = float(value)
1810                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1811            except ValueError:
1812                if value and '-' not in value[0]:
1813                    print 'bad input - numbers only'
1814                    self.FindWindowById(id).SetValue('0.000')
1815           
1816        def OnNameChange(self,event):
1817            self.data[-1] = self.name.GetValue() 
1818           
1819        def OnOk(self,event):
1820            parent = self.GetParent()
1821            parent.Raise()
1822            self.EndModal(wx.ID_OK)             
1823           
1824        def OnCancel(self,event):
1825            parent = self.GetParent()
1826            parent.Raise()
1827            self.EndModal(wx.ID_CANCEL)             
1828           
1829        def GetData(self):
1830            return self.data
1831                       
1832    def OnPwdrSum(self,event):
1833        'Sum together powder data(?)'
1834        TextList = []
1835        DataList = []
1836        SumList = []
1837        Names = []
1838        Inst = None
1839        SumItemList = []
1840        Comments = ['Sum equals: \n']
1841        if self.PatternTree.GetCount():
1842            item, cookie = self.PatternTree.GetFirstChild(self.root)
1843            while item:
1844                name = self.PatternTree.GetItemText(item)
1845                Names.append(name)
1846                if 'PWDR' in name:
1847                    TextList.append([0.0,name])
1848                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
1849                    if not Inst:
1850                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
1851                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1852            if len(TextList) < 2:
1853                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
1854                return
1855            TextList.append('default_sum_name')               
1856            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
1857            try:
1858                if dlg.ShowModal() == wx.ID_OK:
1859                    lenX = 0
1860                    Xminmax = [0,0]
1861                    Xsum = []
1862                    Ysum = []
1863                    Vsum = []
1864                    result = dlg.GetData()
1865                    for i,item in enumerate(result[:-1]):
1866                        scale,name = item
1867                        data = DataList[i]
1868                        if scale:
1869                            Comments.append("%10.3f %s" % (scale,' * '+name))
1870                            x,y,w,yc,yb,yd = data   #numpy arrays!
1871                            v = 1./w
1872                            if lenX:
1873                                if lenX != len(x):
1874                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
1875                                        '\nExpected:'+str(lenX)+ \
1876                                        '\nFound:   '+str(len(x))+'\nfor '+name)
1877                                    return
1878                            else:
1879                                lenX = len(x)
1880                            if Xminmax[1]:
1881                                if Xminmax != [x[0],x[-1]]:
1882                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
1883                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
1884                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
1885                                    return
1886                                else:
1887                                    for j,yi in enumerate(y):
1888                                         Ysum[j] += scale*yi
1889                                         Vsum[j] += abs(scale)*v[j]
1890                            else:
1891                                Xminmax = [x[0],x[-1]]
1892                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
1893                                for j,yi in enumerate(y):
1894                                    Xsum.append(x[j])
1895                                    Ysum.append(scale*yi)
1896                                    Vsum.append(abs(scale*v[j]))
1897                    Wsum = 1./np.array(Vsum)
1898                    outname = 'PWDR '+result[-1]
1899                    Id = 0
1900                    if outname in Names:
1901                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1902                        try:
1903                            if dlg2.ShowModal() == wx.ID_OK:
1904                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1905                                self.PatternTree.Delete(Id)
1906                        finally:
1907                            dlg2.Destroy()
1908                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1909                    if Id:
1910                        Sample = G2pdG.SetDefaultSample()
1911                        valuesdict = {
1912                            'wtFactor':1.0,
1913                            'Dummy':False,
1914                            'ranId':ran.randint(0,sys.maxint),
1915                            }
1916                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
1917                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
1918                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
1919                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
1920                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
1921                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1922                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
1923                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1924                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
1925                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
1926                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1927                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
1928                        self.PatternTree.SelectItem(Id)
1929                        self.PatternTree.Expand(Id)
1930            finally:
1931                dlg.Destroy()
1932
1933    def OnImageSum(self,event):
1934        'Sum together image data(?)'
1935        TextList = []
1936        DataList = []
1937        SumList = []
1938        Names = []
1939        Inst = []
1940        SumItemList = []
1941        Comments = ['Sum equals: \n']
1942        if self.PatternTree.GetCount():
1943            item, cookie = self.PatternTree.GetFirstChild(self.root)
1944            while item:
1945                name = self.PatternTree.GetItemText(item)
1946                Names.append(name)
1947                if 'IMG' in name:
1948                    TextList.append([0.0,name])
1949                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
1950                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
1951                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1952            if len(TextList) < 2:
1953                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
1954                return
1955            TextList.append('default_sum_name')               
1956            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
1957            try:
1958                if dlg.ShowModal() == wx.ID_OK:
1959                    imSize = 0
1960                    result = dlg.GetData()
1961                    First = True
1962                    Found = False
1963                    for i,item in enumerate(result[:-1]):
1964                        scale,name = item
1965                        data = DataList[i]
1966                        if scale:
1967                            Found = True                               
1968                            Comments.append("%10.3f %s" % (scale,' * '+name))
1969                            Npix,imagefile = data
1970                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
1971                            if First:
1972                                newImage = np.zeros_like(image)
1973                                First = False
1974                            if imSize:
1975                                if imSize != Npix:
1976                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
1977                                        '\nExpected:'+str(imSize)+ \
1978                                        '\nFound:   '+str(Npix)+'\nfor '+name)
1979                                    return
1980                                newImage = newImage+scale*image
1981                            else:
1982                                imSize = Npix
1983                                newImage = newImage+scale*image
1984                            del(image)
1985                    if not Found:
1986                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
1987                        return
1988                       
1989                    newImage = np.asfarray(newImage,dtype=np.float32)                       
1990                    outname = 'IMG '+result[-1]
1991                    Id = 0
1992                    if outname in Names:
1993                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1994                        try:
1995                            if dlg2.ShowModal() == wx.ID_OK:
1996                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1997                        finally:
1998                            dlg2.Destroy()
1999                    else:
2000                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2001                    if Id:
2002                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2003                            'G2img files (*.G2img)|*.G2img', 
2004                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2005                        if dlg.ShowModal() == wx.ID_OK:
2006                            newimagefile = dlg.GetPath()
2007                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2008                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2009                            Imax = np.amax(newImage)
2010                            Imin = np.amin(newImage)
2011                            newImage = []
2012                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2013                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2014                        del(newImage)
2015                        if self.imageDefault:
2016                            Data = copy.copy(self.imageDefault)
2017                        Data['showLines'] = True
2018                        Data['ring'] = []
2019                        Data['rings'] = []
2020                        Data['cutoff'] = 10
2021                        Data['pixLimit'] = 20
2022                        Data['ellipses'] = []
2023                        Data['calibrant'] = ''
2024                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2025                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2026                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2027                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2028                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),{})
2029                        self.PatternTree.SelectItem(Id)
2030                        self.PatternTree.Expand(Id)
2031                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2032                        self.Image = self.PickId
2033            finally:
2034                dlg.Destroy()
2035                     
2036    def OnAddPhase(self,event):
2037        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2038        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2039            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2040        else:
2041            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2042        PhaseName = ''
2043        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2044            style=wx.OK)
2045        if dlg.ShowModal() == wx.ID_OK:
2046            PhaseName = dlg.GetValue()
2047        dlg.Destroy()
2048        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2049        E,SGData = G2spc.SpcGroup('P 1')
2050        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2051       
2052    def OnDeletePhase(self,event):
2053        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2054        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2055        if self.dataFrame:
2056            self.dataFrame.Clear() 
2057        TextList = []
2058        DelList = []
2059        DelItemList = []
2060        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2061            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2062        else:
2063            return
2064        if sub:
2065            item, cookie = self.PatternTree.GetFirstChild(sub)
2066            while item:
2067                TextList.append(self.PatternTree.GetItemText(item))
2068                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2069            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2070            try:
2071                if dlg.ShowModal() == wx.ID_OK:
2072                    result = dlg.GetSelections()
2073                    for i in result: DelList.append([i,TextList[i]])
2074                    item, cookie = self.PatternTree.GetFirstChild(sub)
2075                    i = 0
2076                    while item:
2077                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2078                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2079                        i += 1
2080                    for item in DelItemList:
2081                        name = self.PatternTree.GetItemText(item)
2082                        self.PatternTree.Delete(item)
2083                        self.G2plotNB.Delete(name)
2084                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2085                    while item:
2086                        name = self.PatternTree.GetItemText(item)
2087                        if 'PWDR' in name:
2088                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2089                            refList = self.PatternTree.GetItemPyData(Id)
2090                            for i,item in DelList:
2091                                del(refList[item])
2092                            self.PatternTree.SetItemPyData(Id,refList)
2093                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2094            finally:
2095                dlg.Destroy()
2096               
2097    def OnRenameData(self,event):
2098        'Renames an existing phase. Called by Data/Rename Phase menu'
2099        name = self.PatternTree.GetItemText(self.PickId)     
2100        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2101            dataType = name[:name.index(' ')+1]                 #includes the ' '
2102            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2103                defaultValue=name[name.index(' ')+1:])
2104            try:
2105                if dlg.ShowModal() == wx.ID_OK:
2106                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2107            finally:
2108                dlg.Destroy()
2109       
2110    def GetFileList(self,fileType,skip=None):        #potentially useful?
2111        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2112        fileList = []
2113        Source = ''
2114        id, cookie = self.PatternTree.GetFirstChild(self.root)
2115        while id:
2116            name = self.PatternTree.GetItemText(id)
2117            if fileType in name:
2118                if id == skip:
2119                    Source = name
2120                else:
2121                    fileList.append([False,name,id])
2122            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2123        if skip:
2124            return fileList,Source
2125        else:
2126            return fileList
2127           
2128    def OnDataDelete(self, event):
2129        '''Delete one or more histograms from data tree. Called by the
2130        Data/DeleteData menu
2131        '''
2132        TextList = ['All Data']
2133        DelList = []
2134        DelItemList = []
2135        ifPWDR = False
2136        ifIMG = False
2137        ifHKLF = False
2138        ifPDF = False
2139        if self.PatternTree.GetCount():
2140            item, cookie = self.PatternTree.GetFirstChild(self.root)
2141            while item:
2142                name = self.PatternTree.GetItemText(item)
2143                if name not in ['Notebook','Controls','Covariance','Constraints',
2144                    'Restraints','Phases','Rigid bodies']:
2145                    if 'PWDR' in name: ifPWDR = True
2146                    if 'IMG' in name: ifIMG = True
2147                    if 'HKLF' in name: ifHKLF = True
2148                    if 'PDF' in name: ifPDF = True
2149                    TextList.append(name)
2150                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2151            if ifPWDR: TextList.insert(1,'All PWDR')
2152            if ifIMG: TextList.insert(1,'All IMG')
2153            if ifHKLF: TextList.insert(1,'All HKLF')
2154            if ifPDF: TextList.insert(1,'All PDF')               
2155            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2156            try:
2157                if dlg.ShowModal() == wx.ID_OK:
2158                    result = dlg.GetSelections()
2159                    for i in result: DelList.append(TextList[i])
2160                    if 'All Data' in DelList:
2161                        DelList = [item for item in TextList if item[:3] != 'All']
2162                    elif 'All PWDR' in DelList:
2163                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2164                    elif 'All IMG' in DelList:
2165                        DelList = [item for item in TextList if item[:3] == 'IMG']
2166                    elif 'All HKLF' in DelList:
2167                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2168                    elif 'All PDF' in DelList:
2169                        DelList = [item for item in TextList if item[:3] == 'PDF']
2170                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2171                    while item:
2172                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2173                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2174                    for item in DelItemList:
2175                        self.PatternTree.Delete(item)
2176                    self.PickId = 0
2177                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2178            finally:
2179                dlg.Destroy()
2180
2181    def OnFileOpen(self, event, filename=None):
2182        '''Reads in a GSAS-II .gpx project file in response to the
2183        File/Open Project menu button
2184        '''
2185        result = wx.ID_OK
2186        Id = 0
2187        if self.PatternTree.GetChildrenCount(self.root,False):
2188            if self.dataFrame:
2189                self.dataFrame.Clear() 
2190            dlg = wx.MessageDialog(
2191                self,
2192                'Do you want to overwrite the current project? '
2193                'Any unsaved changes will be lost. Press OK to continue.',
2194                'Overwrite?',  wx.OK | wx.CANCEL)
2195            try:
2196                result = dlg.ShowModal()
2197                if result == wx.ID_OK:
2198                    self.PatternTree.DeleteChildren(self.root)
2199                    self.GSASprojectfile = ''
2200                    if self.HKL: self.HKL = []
2201                    if self.G2plotNB.plotList:
2202                        self.G2plotNB.clear()
2203            finally:
2204                dlg.Destroy()
2205        if result != wx.ID_OK: return
2206
2207        if not filename:
2208            if self.dataDisplay: self.dataDisplay.Destroy()
2209            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2210                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2211            try:
2212                if dlg.ShowModal() != wx.ID_OK: return
2213                self.GSASprojectfile = dlg.GetPath()
2214                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2215                self.dirname = dlg.GetDirectory()
2216            finally:
2217                dlg.Destroy()
2218        else:
2219            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2220            self.dirname = os.path.split(filename)[0]
2221
2222        G2IO.ProjFileOpen(self)
2223        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2224        self.PatternTree.Expand(self.root)
2225        self.HKL = []
2226        item, cookie = self.PatternTree.GetFirstChild(self.root)
2227        while item and not Id:
2228            name = self.PatternTree.GetItemText(item)
2229            if name[:4] in ['PWDR','HKLF','IMG ','PDF ']:
2230                Id = item
2231            elif name == 'Controls':
2232                data = self.PatternTree.GetItemPyData(item)
2233                if data:
2234                    for item in self.Refine: item.Enable(True)
2235                    for item in self.SeqRefine: item.Enable(True)
2236            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2237        if Id:
2238            self.PatternTree.SelectItem(Id)
2239        self.CheckNotebook()
2240        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2241
2242    def OnFileClose(self, event):
2243        '''Clears the data tree in response to the
2244        File/New Project menu button. User is given option to save
2245        the project.
2246        '''
2247        if self.dataFrame:
2248            self.dataFrame.Clear()
2249            self.dataFrame.SetLabel('GSAS-II data display') 
2250        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2251        try:
2252            result = dlg.ShowModal()
2253            if result == wx.ID_OK:
2254                self.OnFileSaveMenu(event)
2255            if result != wx.ID_CANCEL:
2256                self.GSASprojectfile = ''
2257                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2258                self.PatternTree.DeleteChildren(self.root)
2259                if self.HKL: self.HKL = []
2260                if self.G2plotNB.plotList:
2261                    self.G2plotNB.clear()
2262        finally:
2263            dlg.Destroy()
2264
2265    def OnFileSave(self, event):
2266        '''Save the current project in response to the
2267        File/Save Project menu button
2268        '''
2269       
2270        if self.GSASprojectfile: 
2271            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2272            G2IO.ProjFileSave(self)
2273        else:
2274            self.OnFileSaveas(event)
2275
2276    def OnFileSaveas(self, event):
2277        '''Save the current project in response to the
2278        File/Save as menu button
2279        '''
2280        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2281            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2282        try:
2283            if dlg.ShowModal() == wx.ID_OK:
2284                self.GSASprojectfile = dlg.GetPath()
2285                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2286                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2287                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2288                G2IO.ProjFileSave(self)
2289                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2290        finally:
2291            dlg.Destroy()
2292
2293    def ExitMain(self, event):
2294        '''Called if the main window is closed'''
2295        if self.undofile:
2296            os.remove(self.undofile)
2297        sys.exit()
2298       
2299    def OnFileExit(self, event):
2300        '''Called in response to the File/Quit menu button'''
2301        if self.dataFrame:
2302            self.dataFrame.Clear() 
2303            self.dataFrame.Destroy()
2304        self.Close()
2305       
2306    def OnExportPatterns(self,event):
2307        names = ['All']
2308        exports = []
2309        item, cookie = self.PatternTree.GetFirstChild(self.root)
2310        while item:
2311            name = self.PatternTree.GetItemText(item)
2312            if 'PWDR' in name:
2313                names.append(name)
2314            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2315        if names:
2316            dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2317            if dlg.ShowModal() == wx.ID_OK:
2318                sel = dlg.GetSelections()
2319                if sel[0] == 0:
2320                    exports = names[1:]
2321                else:
2322                    for x in sel:
2323                        exports.append(names[x])
2324            dlg.Destroy()
2325        if exports:
2326            dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '', 
2327                'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2328                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2329            try:
2330                if dlg.ShowModal() == wx.ID_OK:
2331                    powderfile = dlg.GetPath()
2332                    powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2333                    if 'fxye' in powderfile:
2334                        G2IO.powderFxyeSave(self,exports,powderfile)
2335                    else:       #just xye
2336                        G2IO.powderXyeSave(self,exports,powderfile)
2337            finally:
2338                dlg.Destroy()
2339       
2340    def OnExportPeakList(self,event):
2341        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2342            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2343        try:
2344            if dlg.ShowModal() == wx.ID_OK:
2345                self.peaklistfile = dlg.GetPath()
2346                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2347                file = open(self.peaklistfile,'w')               
2348                item, cookie = self.PatternTree.GetFirstChild(self.root)
2349                while item:
2350                    name = self.PatternTree.GetItemText(item)
2351                    if 'PWDR' in name:
2352                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2353                        while item2:
2354                            name2 = self.PatternTree.GetItemText(item2)
2355                            if name2 == 'Peak List':
2356                                peaks = self.PatternTree.GetItemPyData(item2)
2357                                file.write("%s \n" % (name+' Peak List'))               
2358                                for peak in peaks:
2359                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2360                                        (peak[0],peak[2],peak[4],peak[6]))
2361                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2362                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2363                file.close()
2364        finally:
2365            dlg.Destroy()
2366       
2367    def OnExportHKL(self,event):
2368        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2369            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2370        try:
2371            if dlg.ShowModal() == wx.ID_OK:
2372                self.peaklistfile = dlg.GetPath()
2373                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2374                file = open(self.peaklistfile,'w')               
2375                item, cookie = self.PatternTree.GetFirstChild(self.root)
2376                while item:
2377                    name = self.PatternTree.GetItemText(item)
2378                    if 'PWDR' in name:
2379                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2380                        while item2:
2381                            name2 = self.PatternTree.GetItemText(item2)
2382                            if name2 == 'Reflection Lists':
2383                                data = self.PatternTree.GetItemPyData(item2)
2384                                phases = data.keys()
2385                                for phase in phases:
2386                                    peaks = data[phase]
2387                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2388                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2389                                    for peak in peaks:
2390                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2391                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2392                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2393                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2394                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2395                file.close()
2396        finally:
2397            dlg.Destroy()
2398       
2399    def OnExportPDF(self,event):
2400        #need S(Q) and G(R) to be saved here - probably best from selection?
2401        names = ['All']
2402        exports = []
2403        item, cookie = self.PatternTree.GetFirstChild(self.root)
2404        while item:
2405            name = self.PatternTree.GetItemText(item)
2406            if 'PDF' in name:
2407                names.append(name)
2408            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2409        if names:
2410            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2411            if dlg.ShowModal() == wx.ID_OK:
2412                sel = dlg.GetSelections()
2413                if sel[0] == 0:
2414                    exports = names[1:]
2415                else:
2416                    for x in sel:
2417                        exports.append(names[x])
2418            dlg.Destroy()
2419        if exports:
2420            G2IO.PDFSave(self,exports)
2421       
2422    def OnMakePDFs(self,event):
2423        '''Calculates PDFs
2424        '''
2425        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2426        TextList = ['All PWDR']
2427        PDFlist = []
2428        Names = []
2429        if self.PatternTree.GetCount():
2430            id, cookie = self.PatternTree.GetFirstChild(self.root)
2431            while id:
2432                name = self.PatternTree.GetItemText(id)
2433                Names.append(name)
2434                if 'PWDR' in name:
2435                    TextList.append(name)
2436                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2437            if len(TextList) == 1:
2438                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2439                return
2440            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2441            try:
2442                if dlg.ShowModal() == wx.ID_OK:
2443                    result = dlg.GetSelections()
2444                    for i in result: PDFlist.append(TextList[i])
2445                    if 0 in result:
2446                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2447                    for item in PDFlist:
2448                        PWDRname = item[4:]
2449                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2450                        Data = {
2451                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2452                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2453                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2454                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2455                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2456                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2457                            'Lorch':True,}
2458                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2459                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2460                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2461                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2462                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2463                for item in self.ExportPDF: item.Enable(True)
2464            finally:
2465                dlg.Destroy()
2466               
2467    def GetPWDRdatafromTree(self,PWDRname):
2468        ''' Returns powder data from GSASII tree
2469
2470        :param str PWDRname: a powder histogram name as obtained from
2471          :meth:`GSASIIstruct.GetHistogramNames`
2472
2473        :returns: PWDRdata = powder data dictionary with
2474          Powder data arrays, Limits, Instrument Parameters,
2475          Sample Parameters           
2476        '''
2477        PWDRdata = {}
2478        try:
2479            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2480        except ValueError:
2481            PWDRdata['wtFactor'] = 1.0
2482        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2483        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2484        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2485        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2486        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2487        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2488        if 'ranId' not in PWDRdata:  # patch, add a random Id
2489            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
2490        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
2491            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
2492        return PWDRdata
2493
2494    def GetHKLFdatafromTree(self,HKLFname):
2495        ''' Returns single crystal data from GSASII tree
2496
2497        :param str HKLFname: a single crystal histogram name as obtained
2498          from
2499          :meth:`GSASIIstruct.GetHistogramNames`
2500
2501        :returns: HKLFdata = single crystal data list of reflections
2502
2503        '''
2504        HKLFdata = {}
2505        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2506#        try:
2507#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2508#        except ValueError:
2509#            HKLFdata['wtFactor'] = 1.0
2510        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2511        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2512        return HKLFdata
2513       
2514    def GetPhaseData(self):
2515        '''Returns a dict with defined phases.
2516        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
2517        get same info from GPX file.
2518        '''
2519        phaseData = {}
2520        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2521            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2522        else:
2523            print 'no phases found in GetPhaseData'
2524            sub = None
2525        if sub:
2526            item, cookie = self.PatternTree.GetFirstChild(sub)
2527            while item:
2528                phaseName = self.PatternTree.GetItemText(item)
2529                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2530                if 'ranId' not in phaseData[phaseName]:
2531                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2532                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2533        return phaseData
2534
2535    def GetPhaseNames(self):
2536        '''Returns a list of defined phases.
2537        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
2538        get same info from GPX file.
2539        '''
2540        phaseNames = []
2541        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2542            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2543        else:
2544            print 'no phases found in GetPhaseNames'
2545            sub = None
2546        if sub:
2547            item, cookie = self.PatternTree.GetFirstChild(sub)
2548            while item:
2549                phase = self.PatternTree.GetItemText(item)
2550                phaseNames.append(phase)
2551                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2552        return phaseNames
2553   
2554    def GetHistogramNames(self,hType):
2555        """ Returns a list of histogram names found in the GSASII data tree
2556        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
2557        get same info from GPX file.
2558       
2559        :param str hType: list of histogram types
2560        :return: list of histogram names
2561       
2562        """
2563        HistogramNames = []
2564        if self.PatternTree.GetCount():
2565            item, cookie = self.PatternTree.GetFirstChild(self.root)
2566            while item:
2567                name = self.PatternTree.GetItemText(item)
2568                if name[:4] in hType:
2569                    HistogramNames.append(name)       
2570                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2571
2572        return HistogramNames
2573
2574                   
2575    def GetUsedHistogramsAndPhasesfromTree(self):
2576        ''' Returns all histograms that are found in any phase
2577        and any phase that uses a histogram.
2578        This also assigns numbers to used phases and histograms by the
2579        order they appear in the file.
2580        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
2581        get same info from GPX file.
2582
2583        :returns: (Histograms,Phases)
2584
2585            * Histograms = dictionary of histograms as {name:data,...}
2586            * Phases = dictionary of phases that use histograms
2587        '''
2588        Histograms = {}
2589        Phases = {}
2590        phaseNames = self.GetPhaseNames()
2591        phaseData = self.GetPhaseData()
2592        histoList = self.GetHistogramNames(['PWDR','HKLF'])
2593
2594        for phase in phaseData:
2595            Phase = phaseData[phase]
2596            pId = phaseNames.index(phase)
2597            Phase['pId'] = pId
2598            if Phase['Histograms']:
2599                if phase not in Phases:
2600                    Phases[phase] = Phase
2601                for hist in Phase['Histograms']:
2602                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
2603                        Phase['Histograms'][hist]['Use'] = True         
2604                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
2605                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
2606                        if item:
2607                            if 'PWDR' in hist[:4]: 
2608                                Histograms[hist] = self.GetPWDRdatafromTree(item)
2609                            elif 'HKLF' in hist[:4]:
2610                                Histograms[hist] = self.GetHKLFdatafromTree(item)
2611                            hId = histoList.index(hist)
2612                            Histograms[hist]['hId'] = hId
2613                        else: # would happen if a referenced histogram were renamed or deleted
2614                            print('For phase "'+str(phase)+
2615                                  '" unresolved reference to histogram "'+str(hist)+'"')
2616        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
2617        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
2618        return Histograms,Phases
2619       
2620    def MakeLSParmDict(self):
2621        '''Load all parameters used for computation from the tree into a
2622        dict of paired values [value, refine flag]. Note that this is
2623        different than the parmDict used in the refinement, which only has
2624        values.
2625
2626        :returns: (parmDict,varyList) where:
2627
2628         * parmDict is a dict with values and refinement flags
2629           for each parameter and
2630         * varyList is a list of variables (refined parameters).
2631        '''
2632        parmDict = {}
2633        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
2634        for phase in Phases:
2635            if 'pId' not in Phases[phase]:
2636                self.ErrorDialog('View parameter error','You must run least squares at least once')
2637                raise Exception,'No pId for phase '+str(phase)
2638        rigidbodyDict = self.PatternTree.GetItemPyData(   
2639            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
2640        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2641        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2642        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
2643        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
2644        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
2645        varyList = rbVary+phaseVary+hapVary+histVary
2646        parmDict.update(rbDict)
2647        parmDict.update(phaseDict)
2648        parmDict.update(hapDict)
2649        parmDict.update(histDict)
2650        for parm in parmDict:
2651            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
2652                'Omega','Chi','Phi','nDebye','nPeaks']:
2653                parmDict[parm] = [parmDict[parm],'-']
2654            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
2655                parmDict[parm] = [parmDict[parm],'-']
2656            elif parm in varyList:
2657                parmDict[parm] = [parmDict[parm],'T']
2658            else:
2659                parmDict[parm] = [parmDict[parm],'F']
2660        return parmDict,varyList
2661
2662    def ShowLSParms(self,event):
2663        '''Displays a window showing all parameters in the refinement.
2664        Called from the Calculate/View LS Parms menu.
2665        '''
2666        parmDict,varyList = self.MakeLSParmDict()
2667        parmValDict = {}
2668        for i in parmDict:
2669            parmValDict[i] = parmDict[i][0]
2670           
2671        reqVaryList = tuple(varyList) # save requested variables
2672        try:
2673            # process constraints
2674            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
2675            Constraints = self.PatternTree.GetItemPyData(sub)
2676            constList = []
2677            for item in Constraints:
2678                if item.startswith('_'): continue
2679                constList += Constraints[item]
2680            G2mv.InitVars()
2681            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2682            groups,parmlist = G2mv.GroupConstraints(constrDict)
2683            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList)
2684            G2mv.Map2Dict(parmValDict,varyList)
2685        except:
2686            pass
2687        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
2688        dlg.ShowModal()
2689        dlg.Destroy()
2690       
2691    def OnRefine(self,event):
2692        '''Perform a refinement.
2693        Called from the Calculate/Refine menu.
2694        '''       
2695        self.OnFileSave(event)
2696        # check that constraints are OK here
2697        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2698        if errmsg:
2699            print('Error in constraints:\n'+errmsg+
2700                  '\nRefinement not possible')
2701            self.ErrorDialog('Constraint Error',
2702                             'Error in constraints:\n'+errmsg+
2703                             '\nRefinement not possible')
2704            return
2705        if warnmsg:
2706            print('Conflict between refinment flag settings and constraints:\n'+
2707                  warnmsg+'\nRefinement not possible')
2708            self.ErrorDialog('Refinement Flag Error',
2709                             'Conflict between refinment flag settings and constraints:\n'+
2710                             warnmsg+
2711                             '\nRefinement not possible')
2712            return
2713        #works - but it'd be better if it could restore plots
2714        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
2715            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2716        screenSize = wx.ClientDisplayRect()
2717        Size = dlg.GetSize()
2718        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2719        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2720        dlg.SetSize(Size)
2721        Rw = 100.00
2722        try:
2723            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
2724        finally:
2725            dlg.Destroy()
2726        oldId =  self.PatternTree.GetSelection()
2727        oldName = self.PatternTree.GetItemText(oldId)
2728        parentId = self.PatternTree.GetItemParent(oldId)
2729        parentName = ''
2730        if parentId:
2731            parentName = self.PatternTree.GetItemText(parentId)
2732        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
2733        try:
2734            if dlg.ShowModal() == wx.ID_OK:
2735                Id = 0
2736                self.PatternTree.DeleteChildren(self.root)
2737                if self.HKL: self.HKL = []
2738                if self.G2plotNB.plotList:
2739                    self.G2plotNB.clear()
2740                G2IO.ProjFileOpen(self)
2741                item, cookie = self.PatternTree.GetFirstChild(self.root)
2742                while item and not Id:
2743                    name = self.PatternTree.GetItemText(item)
2744                    if name[:4] in ['PWDR','HKLF']:
2745                        Id = item
2746                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2747                if parentName:
2748                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
2749                    if parentId:
2750                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
2751                    else:
2752                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
2753                    self.PatternTree.SelectItem(itemId)
2754                elif Id:
2755                    self.PatternTree.SelectItem(Id)
2756        finally:
2757            dlg.Destroy()
2758
2759    def OnSeqRefine(self,event):
2760        '''Perform a sequential refinement.
2761        Called from the Calculate/Sequential refine menu.
2762        '''       
2763        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
2764        if not Id:
2765            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
2766            self.PatternTree.SetItemPyData(Id,{})           
2767        self.OnFileSave(event)
2768        # check that constraints are OK here
2769        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2770        if errmsg:
2771            print('Error in constraints:\n'+errmsg+
2772                  '\nRefinement not possible')
2773            self.ErrorDialog('Constraint Error',
2774                             'Error in constraints:\n'+errmsg+
2775                             '\nRefinement not possible')
2776            return
2777        if warnmsg:
2778            print('Conflict between refinment flag settings and constraints:\n'+
2779                  warnmsg+'\nRefinement not possible')
2780            self.ErrorDialog('Refinement Flag Error',
2781                             'Conflict between refinment flag settings and constraints:\n'+
2782                             warnmsg+'\nRefinement not possible')
2783            return
2784        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
2785            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2786        screenSize = wx.ClientDisplayRect()
2787        Size = dlg.GetSize()
2788        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2789        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2790        dlg.SetSize(Size)
2791        try:
2792            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
2793        finally:
2794            dlg.Destroy()       
2795        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
2796        try:
2797            if dlg.ShowModal() == wx.ID_OK:
2798                Id = 0
2799                self.PatternTree.DeleteChildren(self.root)
2800                if self.HKL: self.HKL = []
2801                if self.G2plotNB.plotList:
2802                    self.G2plotNB.clear()
2803                G2IO.ProjFileOpen(self)
2804                item, cookie = self.PatternTree.GetFirstChild(self.root)
2805                while item and not Id:
2806                    name = self.PatternTree.GetItemText(item)
2807                    if name[:4] in ['PWDR','HKLF']:
2808                        Id = item
2809                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2810                if Id:
2811                    self.PatternTree.SelectItem(Id)
2812        finally:
2813            dlg.Destroy()
2814       
2815    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
2816        'Display an error message'
2817        result = None
2818        if parent is None:
2819            dlg = wx.MessageDialog(self, message, title,  wtype)
2820        else:
2821            dlg = wx.MessageDialog(parent, message, title,  wtype)
2822            dlg.CenterOnParent() # not working on Mac
2823        try:
2824            result = dlg.ShowModal()
2825        finally:
2826            dlg.Destroy()
2827        return result
2828
2829class GSASIImain(wx.App):
2830    '''Defines a wxApp for GSAS-II
2831
2832    Creates a wx frame (self.main) which contains the display of the
2833    data tree.
2834    '''
2835    def OnInit(self):
2836        '''Called automatically when the app is created.'''
2837        self.main = GSASII(None)
2838        self.main.Show()
2839        self.SetTopWindow(self.main)
2840        return True
2841    def MacOpenFile(self, filename):
2842        '''Called on Mac every time a file is dropped on the app when it is running,
2843        treat this like a File/Open project menu action.
2844        Should be ignored on other platforms
2845        '''
2846        self.main.OnFileOpen(None,filename)
2847
2848def main():
2849    '''Start up the GSAS-II application'''
2850    #application = GSASIImain() # don't redirect output, someday we
2851    # may want to do this if we can
2852    application = GSASIImain(0)
2853    if wxInspector:
2854        import wx.lib.inspection as wxeye
2855        wxeye.InspectionTool().Show()
2856
2857    #application.main.OnRefine(None)
2858    application.MainLoop()
2859   
2860if __name__ == '__main__':
2861    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.