source: trunk/GSASII.py @ 2034

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

remove block of commented out old code from OnRefine?
fix contour slider for 4D maps

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