source: trunk/GSASII.py @ 1296

Last change on this file since 1296 was 1296, checked in by vondreele, 8 years ago

another EnablePlot? for OnFileOpen?

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