source: trunk/GSASII.py @ 1257

Last change on this file since 1257 was 1257, checked in by toby, 8 years ago

change selector for hist. copy & copy flags; change seq ref hist select; change enabling of seq ref menu item; rework G2MultiChoiceDialog to save selections and add SetSelections?

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