Ignore:
Timestamp:
Jul 3, 2017 4:12:45 PM (6 years ago)
Author:
toby
Message:

partial reorg

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branch/2frame/GSASIIgrid.py

    r2898 r2899  
    1313
    1414'''
     15import time
     16import math
     17import random as ran
     18import copy
     19import sys
     20import os
     21import glob
     22import random as ran
     23import imp
     24import inspect
     25import numpy as np
     26import numpy.ma as ma
     27import matplotlib as mpl
     28import OpenGL as ogl
     29import scipy as sp
     30import scipy.optimize as so
    1531import wx
    1632import wx.grid as wg
     
    1834#import wx.aui
    1935import wx.lib.scrolledpanel as wxscroll
    20 import time
    21 import copy
    22 import sys
    23 import os
    24 import random as ran
    25 import numpy as np
    26 import numpy.ma as ma
    27 import scipy.optimize as so
    2836import GSASIIpath
    2937GSASIIpath.SetVersionNumber("$Revision$")
     
    5159cosd = lambda x: np.cos(x*np.pi/180.)
    5260
    53 # Define a short name for convenience
     61# Define short names for convenience
    5462WACV = wx.ALIGN_CENTER_VERTICAL
    55 [ wxID_FOURCALC, wxID_FOURSEARCH, wxID_FOURCLEAR, wxID_PEAKSMOVE, wxID_PEAKSCLEAR,
    56     wxID_CHARGEFLIP, wxID_PEAKSUNIQUE, wxID_PEAKSDELETE, wxID_PEAKSDA,
    57     wxID_PEAKSDISTVP, wxID_PEAKSVIEWPT, wxID_FINDEQVPEAKS,wxID_SHOWBONDS,wxID_MULTIMCSA,
    58     wxID_SINGLEMCSA,wxID_4DCHARGEFLIP,wxID_TRANSFORMSTRUCTURE,
    59 ] = [wx.NewId() for item in range(17)]
    60 
    61 [ wxID_PWDRADD, wxID_HKLFADD, wxID_PWDANALYSIS, wxID_PWDCOPY, wxID_PLOTCTRLCOPY,
    62     wxID_DATADELETE,wxID_DATACOPY,wxID_DATACOPYFLAGS,wxID_DATASELCOPY,wxID_DATAUSE,
    63 ] = [wx.NewId() for item in range(10)]
    64 
    65 [ wxID_ATOMSEDITADD, wxID_ATOMSEDITINSERT, wxID_ATOMSEDITDELETE,
    66     wxID_ATOMSMODIFY, wxID_ATOMSTRANSFORM, wxID_ATOMSVIEWADD, wxID_ATOMVIEWINSERT,
    67     wxID_RELOADDRAWATOMS,wxID_ATOMSDISAGL,wxID_ATOMMOVE,wxID_MAKEMOLECULE,
    68     wxID_ASSIGNATMS2RB,wxID_ATOMSPDISAGL, wxID_ISODISP,wxID_ADDHATOM,wxID_UPDATEHATOM,
    69     wxID_WAVEVARY,wxID_ATOMSROTATE, wxID_ATOMSDENSITY, wxID_VALIDPROTEIN,
    70     wxID_ATOMSSETALL, wxID_ATOMSSETSEL,
    71 ] = [wx.NewId() for item in range(22)]
    72 
    73 [ wxID_DRAWATOMSTYLE, wxID_DRAWATOMLABEL, wxID_DRAWATOMCOLOR, wxID_DRAWATOMRESETCOLOR,
    74     wxID_DRAWVIEWPOINT, wxID_DRAWTRANSFORM, wxID_DRAWDELETE, wxID_DRAWFILLCELL,
    75     wxID_DRAWADDEQUIV, wxID_DRAWFILLCOORD, wxID_DRAWDISAGLTOR,  wxID_DRAWPLANE,
    76     wxID_DRAWDISTVP, wxID_DRAWADDSPHERE,wxID_DRWAEDITRADII,
    77 ] = [wx.NewId() for item in range(15)]
    78 
    79 [ wxID_DRAWRESTRBOND, wxID_DRAWRESTRANGLE, wxID_DRAWRESTRPLANE, wxID_DRAWRESTRCHIRAL,
    80 ] = [wx.NewId() for item in range(4)]
    81 
    82 [ wxID_ADDMCSAATOM,wxID_ADDMCSARB,wxID_CLEARMCSARB,wxID_MOVEMCSA,wxID_MCSACLEARRESULTS,
    83 ] = [wx.NewId() for item in range(5)]
    84 
    85 [ wxID_CLEARTEXTURE,wxID_REFINETEXTURE,
    86 ] = [wx.NewId() for item in range(2)]
    87 
    88 [ wxID_LOADDIFFAX,wxID_LAYERSIMULATE,wxID_SEQUENCESIMULATE, wxID_LAYERSFIT, wxID_COPYPHASE,
    89 ] = [wx.NewId() for item in range(5)]
    90 
    91 [ wxID_PAWLEYLOAD, wxID_PAWLEYESTIMATE, wxID_PAWLEYUPDATE, wxID_PAWLEYSELALL, wxID_PAWLEYSELNONE,
    92   wxID_PAWLEYSELTOGGLE, wxID_PAWLEYSET,
    93 ] = [wx.NewId() for item in range(7)]
    94 
    95 [ wxID_IMCALIBRATE,wxID_IMRECALIBRATE,wxID_IMINTEGRATE, wxID_IMCLEARCALIB,wxID_IMRECALIBALL, 
    96     wxID_IMCOPYCONTROLS, wxID_INTEGRATEALL, wxID_IMSAVECONTROLS, wxID_IMLOADCONTROLS, wxID_IMAUTOINTEG,
    97     wxID_IMCOPYSELECTED, wxID_SAVESELECTEDCONTROLS, wxID_IMXFERCONTROLS,wxID_IMRESETDIST,
    98 ] = [wx.NewId() for item in range(14)]
    99 
    100 [ wxID_MASKCOPY, wxID_MASKSAVE, wxID_MASKLOAD, wxID_NEWMASKSPOT,wxID_NEWMASKARC,wxID_NEWMASKRING,
    101     wxID_NEWMASKFRAME, wxID_NEWMASKPOLY,wxID_MASKLOADNOT,wxID_FINDSPOTS,wxID_DELETESPOTS
    102 ] = [wx.NewId() for item in range(11)]
    103 
    104 [ wxID_STRSTACOPY, wxID_STRSTAFIT, wxID_STRSTASAVE, wxID_STRSTALOAD,wxID_STRSTSAMPLE,
    105     wxID_APPENDDZERO,wxID_STRSTAALLFIT,wxID_UPDATEDZERO,wxID_STRSTAPLOT,wxID_STRRINGSAVE,
    106 ] = [wx.NewId() for item in range(10)]
    107 
    108 [ wxID_BACKCOPY,wxID_LIMITCOPY, wxID_SAMPLECOPY, wxID_SAMPLECOPYSOME, wxID_BACKFLAGCOPY, wxID_SAMPLEFLAGCOPY,
    109     wxID_SAMPLESAVE, wxID_SAMPLELOAD,wxID_ADDEXCLREGION,wxID_SETSCALE,wxID_SAMPLE1VAL,wxID_ALLSAMPLELOAD,
    110     wxID_MAKEBACKRDF,wxID_RESCALEALL,
    111 ] = [wx.NewId() for item in range(14)]
    112 
    113 [ wxID_INSTPRMRESET,wxID_CHANGEWAVETYPE,wxID_INSTCOPY, wxID_INSTFLAGCOPY, wxID_INSTLOAD,
    114     wxID_INSTSAVE, wxID_INST1VAL, wxID_INSTCALIB,wxID_INSTSAVEALL,
    115 ] = [wx.NewId() for item in range(9)]
    116 
    117 [ wxID_UNDO,wxID_LSQPEAKFIT,wxID_LSQONECYCLE,wxID_RESETSIGGAM,wxID_CLEARPEAKS,wxID_AUTOSEARCH,
    118     wxID_PEAKSCOPY, wxID_SEQPEAKFIT,
    119 ] = [wx.NewId() for item in range(8)]
    120 
    121 [  wxID_INDXRELOAD, wxID_INDEXPEAKS, wxID_REFINECELL, wxID_COPYCELL, wxID_MAKENEWPHASE,
    122     wxID_EXPORTCELLS,
    123 ] = [wx.NewId() for item in range(6)]
    124 
    125 [ wxID_CONSTRAINTADD,wxID_EQUIVADD,wxID_HOLDADD,wxID_FUNCTADD,wxID_ADDRIDING,
    126   wxID_CONSPHASE, wxID_CONSHIST, wxID_CONSHAP, wxID_CONSGLOBAL,wxID_EQUIVALANCEATOMS,
    127 ] = [wx.NewId() for item in range(10)]
    128 
    129 [ wxID_RESTRAINTADD, wxID_RESTSELPHASE,wxID_RESTDELETE, wxID_RESRCHANGEVAL,
    130     wxID_RESTCHANGEESD,wxID_AARESTRAINTADD,wxID_AARESTRAINTPLOT,
    131 ] = [wx.NewId() for item in range(7)]
    132 
    133 [ wxID_RIGIDBODYADD,wxID_DRAWDEFINERB,wxID_RIGIDBODYIMPORT,wxID_RESIDUETORSSEQ,
    134     wxID_AUTOFINDRESRB,wxID_GLOBALRESREFINE,wxID_RBREMOVEALL,wxID_COPYRBPARMS,
    135     wxID_GLOBALTHERM,wxID_VECTORBODYADD
    136 ] = [wx.NewId() for item in range(10)]
    137 
    138 [ wxID_RENAMESEQSEL,wxID_SAVESEQSEL,wxID_SAVESEQSELCSV,wxID_SAVESEQCSV,wxID_PLOTSEQSEL,
    139   wxID_ORGSEQSEL,wxADDSEQVAR,wxDELSEQVAR,wxEDITSEQVAR,wxCOPYPARFIT,wxID_AVESEQSEL,
    140   wxADDPARFIT,wxDELPARFIT,wxEDITPARFIT,wxDOPARFIT,wxADDSEQDIST,wxADDSEQANGLE,wxID_ORGSEQINC,
    141 ] = [wx.NewId() for item in range(18)]
    142 
    143 [ wxID_MODELCOPY,wxID_MODELFIT,wxID_MODELADD,wxID_ELEMENTADD,wxID_ELEMENTDELETE,
    144     wxID_ADDSUBSTANCE,wxID_LOADSUBSTANCE,wxID_DELETESUBSTANCE,wxID_COPYSUBSTANCE,
    145     wxID_MODELUNDO,wxID_MODELFITALL,wxID_MODELCOPYFLAGS,wxID_RELOADSUBSTANCES,
    146     wxID_MODELPLOT,
    147 ] = [wx.NewId() for item in range(14)]
    148 
    149 [ wxID_SELECTPHASE,wxID_PWDHKLPLOT,wxID_PWD3DHKLPLOT,wxID_3DALLHKLPLOT,wxID_MERGEHKL,
    150 ] = [wx.NewId() for item in range(5)]
    151 
    152 [ wxID_PDFCOPYCONTROLS, wxID_PDFSAVECONTROLS, wxID_PDFLOADCONTROLS, wxID_PDFCOMPUTE,
    153     wxID_PDFCOMPUTEALL, wxID_PDFADDELEMENT, wxID_PDFDELELEMENT, wxID_PDFPKSFIT,
    154     wxID_PDFPKSFITALL,wxID_PDFCOPYPEAKS,wxID_CLEARPDFPEAKS,
    155 ] = [wx.NewId() for item in range(11)]
    156 
    157 [ wxID_MCRON,wxID_MCRLIST,wxID_MCRSAVE,wxID_MCRPLAY,
    158 ] = [wx.NewId() for item in range(4)]
    159 
    160 
    161 
    16263VERY_LIGHT_GREY = wx.Colour(235,235,235)
     64
     65# define Ids for wx menu items
     66def Define_wxId(*args):
     67    '''routine to create unique global wx Id symbols in current module.
     68    '''
     69    for arg in args:
     70        if arg in globals():
     71            if GSASIIpath.GetConfigValue('debug'): print arg,'already defined'
     72            continue
     73        exec('global '+arg+';'+arg+' = wx.NewId()')
     74
     75# probably a good idea to move these to where they are used for cleaner code
     76Define_wxId('wxID_FOURCALC', 'wxID_FOURSEARCH', 'wxID_FOURCLEAR', 'wxID_PEAKSMOVE', 'wxID_PEAKSCLEAR',
     77    'wxID_CHARGEFLIP', 'wxID_PEAKSUNIQUE', 'wxID_PEAKSDELETE', 'wxID_PEAKSDA',
     78    'wxID_PEAKSDISTVP', 'wxID_PEAKSVIEWPT', 'wxID_FINDEQVPEAKS', 'wxID_SHOWBONDS', 'wxID_MULTIMCSA',
     79    'wxID_SINGLEMCSA', 'wxID_4DCHARGEFLIP', 'wxID_TRANSFORMSTRUCTURE',)
     80
     81Define_wxId('wxID_PWDRADD', 'wxID_HKLFADD', 'wxID_PWDANALYSIS', 'wxID_PWDCOPY', 'wxID_PLOTCTRLCOPY',
     82    'wxID_DATADELETE', 'wxID_DATACOPY', 'wxID_DATACOPYFLAGS', 'wxID_DATASELCOPY', 'wxID_DATAUSE',)
     83
     84Define_wxId('wxID_ATOMSEDITADD', 'wxID_ATOMSEDITINSERT', 'wxID_ATOMSEDITDELETE',
     85    'wxID_ATOMSMODIFY', 'wxID_ATOMSTRANSFORM', 'wxID_ATOMSVIEWADD', 'wxID_ATOMVIEWINSERT',
     86    'wxID_RELOADDRAWATOMS', 'wxID_ATOMSDISAGL', 'wxID_ATOMMOVE', 'wxID_MAKEMOLECULE',
     87    'wxID_ASSIGNATMS2RB', 'wxID_ATOMSPDISAGL', 'wxID_ISODISP', 'wxID_ADDHATOM', 'wxID_UPDATEHATOM',
     88    'wxID_WAVEVARY', 'wxID_ATOMSROTATE', 'wxID_ATOMSDENSITY', 'wxID_VALIDPROTEIN',
     89    'wxID_ATOMSSETALL', 'wxID_ATOMSSETSEL',)
     90
     91Define_wxId('wxID_DRAWATOMSTYLE', 'wxID_DRAWATOMLABEL', 'wxID_DRAWATOMCOLOR', 'wxID_DRAWATOMRESETCOLOR',
     92    'wxID_DRAWVIEWPOINT', 'wxID_DRAWTRANSFORM', 'wxID_DRAWDELETE', 'wxID_DRAWFILLCELL',
     93    'wxID_DRAWADDEQUIV', 'wxID_DRAWFILLCOORD', 'wxID_DRAWDISAGLTOR', ' wxID_DRAWPLANE',
     94    'wxID_DRAWDISTVP', 'wxID_DRAWADDSPHERE', 'wxID_DRWAEDITRADII',)
     95
     96Define_wxId('wxID_DRAWRESTRBOND', 'wxID_DRAWRESTRANGLE', 'wxID_DRAWRESTRPLANE', 'wxID_DRAWRESTRCHIRAL',)
     97
     98Define_wxId('wxID_ADDMCSAATOM', 'wxID_ADDMCSARB', 'wxID_CLEARMCSARB', 'wxID_MOVEMCSA', 'wxID_MCSACLEARRESULTS',)
     99
     100Define_wxId('wxID_CLEARTEXTURE', 'wxID_REFINETEXTURE',)
     101
     102Define_wxId('wxID_LOADDIFFAX', 'wxID_LAYERSIMULATE', 'wxID_SEQUENCESIMULATE', 'wxID_LAYERSFIT', 'wxID_COPYPHASE',)
     103
     104Define_wxId('wxID_PAWLEYLOAD', 'wxID_PAWLEYESTIMATE', 'wxID_PAWLEYUPDATE', 'wxID_PAWLEYSELALL', 'wxID_PAWLEYSELNONE',
     105  'wxID_PAWLEYSELTOGGLE', 'wxID_PAWLEYSET',)
     106
     107Define_wxId('wxID_IMCALIBRATE', 'wxID_IMRECALIBRATE', 'wxID_IMINTEGRATE', 'wxID_IMCLEARCALIB', 'wxID_IMRECALIBALL',
     108    'wxID_IMCOPYCONTROLS', 'wxID_INTEGRATEALL', 'wxID_IMSAVECONTROLS', 'wxID_IMLOADCONTROLS', 'wxID_IMAUTOINTEG',
     109    'wxID_IMCOPYSELECTED', 'wxID_SAVESELECTEDCONTROLS', 'wxID_IMXFERCONTROLS', 'wxID_IMRESETDIST',)
     110
     111Define_wxId('wxID_MASKCOPY', 'wxID_MASKSAVE', 'wxID_MASKLOAD', 'wxID_NEWMASKSPOT', 'wxID_NEWMASKARC', 'wxID_NEWMASKRING',
     112    'wxID_NEWMASKFRAME', 'wxID_NEWMASKPOLY', 'wxID_MASKLOADNOT', 'wxID_FINDSPOTS', 'wxID_DELETESPOTS',)
     113
     114Define_wxId('wxID_STRSTACOPY', 'wxID_STRSTAFIT', 'wxID_STRSTASAVE', 'wxID_STRSTALOAD', 'wxID_STRSTSAMPLE',
     115    'wxID_APPENDDZERO', 'wxID_STRSTAALLFIT', 'wxID_UPDATEDZERO', 'wxID_STRSTAPLOT', 'wxID_STRRINGSAVE',)
     116
     117Define_wxId('wxID_BACKCOPY', 'wxID_LIMITCOPY', 'wxID_SAMPLECOPY', 'wxID_SAMPLECOPYSOME', 'wxID_BACKFLAGCOPY', 'wxID_SAMPLEFLAGCOPY',
     118    'wxID_SAMPLESAVE', 'wxID_SAMPLELOAD', 'wxID_ADDEXCLREGION', 'wxID_SETSCALE', 'wxID_SAMPLE1VAL', 'wxID_ALLSAMPLELOAD',
     119    'wxID_MAKEBACKRDF', 'wxID_RESCALEALL',)
     120
     121Define_wxId('wxID_INSTPRMRESET', 'wxID_CHANGEWAVETYPE', 'wxID_INSTCOPY', 'wxID_INSTFLAGCOPY', 'wxID_INSTLOAD',
     122    'wxID_INSTSAVE', 'wxID_INST1VAL', 'wxID_INSTCALIB', 'wxID_INSTSAVEALL',)
     123
     124Define_wxId('wxID_UNDO', 'wxID_LSQPEAKFIT', 'wxID_LSQONECYCLE', 'wxID_RESETSIGGAM', 'wxID_CLEARPEAKS', 'wxID_AUTOSEARCH',
     125    'wxID_PEAKSCOPY', 'wxID_SEQPEAKFIT',)
     126
     127Define_wxId(' wxID_INDXRELOAD', 'wxID_INDEXPEAKS', 'wxID_REFINECELL', 'wxID_COPYCELL', 'wxID_MAKENEWPHASE',
     128    'wxID_EXPORTCELLS',)
     129
     130Define_wxId('wxID_CONSTRAINTADD', 'wxID_EQUIVADD', 'wxID_HOLDADD', 'wxID_FUNCTADD', 'wxID_ADDRIDING',
     131  'wxID_CONSPHASE', 'wxID_CONSHIST', 'wxID_CONSHAP', 'wxID_CONSGLOBAL', 'wxID_EQUIVALANCEATOMS',)
     132
     133Define_wxId('wxID_RESTRAINTADD', 'wxID_RESTSELPHASE', 'wxID_RESTDELETE', 'wxID_RESRCHANGEVAL',
     134    'wxID_RESTCHANGEESD', 'wxID_AARESTRAINTADD', 'wxID_AARESTRAINTPLOT',)
     135
     136Define_wxId('wxID_RIGIDBODYADD', 'wxID_DRAWDEFINERB', 'wxID_RIGIDBODYIMPORT', 'wxID_RESIDUETORSSEQ',
     137    'wxID_AUTOFINDRESRB', 'wxID_GLOBALRESREFINE', 'wxID_RBREMOVEALL', 'wxID_COPYRBPARMS',
     138    'wxID_GLOBALTHERM', 'wxID_VECTORBODYADD')
     139
     140Define_wxId('wxID_RENAMESEQSEL', 'wxID_SAVESEQSEL', 'wxID_SAVESEQSELCSV', 'wxID_SAVESEQCSV', 'wxID_PLOTSEQSEL',
     141  'wxID_ORGSEQSEL', 'wxADDSEQVAR', 'wxDELSEQVAR', 'wxEDITSEQVAR', 'wxCOPYPARFIT', 'wxID_AVESEQSEL',
     142  'wxADDPARFIT', 'wxDELPARFIT', 'wxEDITPARFIT', 'wxDOPARFIT', 'wxADDSEQDIST', 'wxADDSEQANGLE', 'wxID_ORGSEQINC',)
     143
     144Define_wxId('wxID_MODELCOPY', 'wxID_MODELFIT', 'wxID_MODELADD', 'wxID_ELEMENTADD', 'wxID_ELEMENTDELETE',
     145    'wxID_ADDSUBSTANCE', 'wxID_LOADSUBSTANCE', 'wxID_DELETESUBSTANCE', 'wxID_COPYSUBSTANCE',
     146    'wxID_MODELUNDO', 'wxID_MODELFITALL', 'wxID_MODELCOPYFLAGS', 'wxID_RELOADSUBSTANCES',
     147    'wxID_MODELPLOT',)
     148
     149Define_wxId('wxID_SELECTPHASE', 'wxID_PWDHKLPLOT', 'wxID_PWD3DHKLPLOT', 'wxID_3DALLHKLPLOT', 'wxID_MERGEHKL',)
     150
     151Define_wxId('wxID_PDFCOPYCONTROLS', 'wxID_PDFSAVECONTROLS', 'wxID_PDFLOADCONTROLS', 'wxID_PDFCOMPUTE',
     152    'wxID_PDFCOMPUTEALL', 'wxID_PDFADDELEMENT', 'wxID_PDFDELELEMENT', 'wxID_PDFPKSFIT',
     153    'wxID_PDFPKSFITALL', 'wxID_PDFCOPYPEAKS', 'wxID_CLEARPDFPEAKS',)
     154
     155Define_wxId('wxID_MCRON', 'wxID_MCRLIST', 'wxID_MCRSAVE', 'wxID_MCRPLAY',)
    163156
    164157commonTrans = {'abc':np.eye(3),'a-cb':np.array([[1.,0.,0.],[0.,0.,-1.],[0.,1.,0.]]),
     
    176169    'P->I','I->P','P->F','F->P','H->R','R->H','R->O','O->R','abc*','setting 1->2']          #don't put any new ones after the setting one!
    177170
     171def SetDefaultDData(dType,histoName,NShkl=0,NDij=0):
     172    if dType in ['SXC','SNC']:
     173        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
     174            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
     175            'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
     176            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}],
     177            'Flack':[0.0,False]}
     178    elif dType == 'SNT':
     179        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
     180            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
     181            'Extinction':['Lorentzian','None', {
     182            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}]}
     183    elif 'P' in dType:
     184        return {'Histogram':histoName,'Show':False,'Scale':[1.0,False],
     185            'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{},[],0.1],
     186            'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
     187                [1.,1.,1.,0.,0.,0.],6*[False,]],
     188            'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
     189                NShkl*[0.01,],NShkl*[False,]],
     190            'HStrain':[NDij*[0.0,],NDij*[False,]],                         
     191            'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
     192
    178193################################################################################
    179 #### GSAS-II class definitions
     194#### class definitions used for main GUI
    180195################################################################################
    181196
    182 # Should SymOpDialog, DisAglDialog etc. be moved to GSASIIctrls?
    183 
    184 class SGMagSpinBox(wx.Dialog):
    185     ''' Special version of MessageBox that displays magnetic spin text
    186     '''
    187     def __init__(self,parent,title,text,table,names,spins,):
    188         wx.Dialog.__init__(self,parent,wx.ID_ANY,title,pos=wx.DefaultPosition,
    189             style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,size=wx.Size(420,350))
    190         self.text = text
    191         self.table = table
    192         self.names = names
    193         self.spins = spins
    194         self.panel = wxscroll.ScrolledPanel(self)
    195         mainSizer = wx.BoxSizer(wx.VERTICAL)
    196         mainSizer.Add((0,10))
    197         first = text[0].split(':')[-1].strip()
    198         cents = [0,]
    199         if 'P' != first[0]:
    200             cents = text[-1].split(';')
    201         for line in text:
    202             mainSizer.Add(wx.StaticText(self.panel,label='     %s     '%(line)),0,WACV)
    203         ncol = self.table[0].count(',')+2
    204         for ic,cent in enumerate(cents):
    205             if cent:
    206                 cent = cent.strip(' (').strip(')+\n')
    207                 mainSizer.Add(wx.StaticText(self.panel,label=' for (%s)+'%(cent)),0,WACV)
    208             tableSizer = wx.FlexGridSizer(0,2*ncol+3,0,0)
    209             for j,item in enumerate(self.table):
    210                 flds = item.split(')')[1]
    211                 tableSizer.Add(wx.StaticText(self.panel,label='  (%2d)  '%(j+1)),0,WACV|wx.ALIGN_LEFT)           
    212                 flds = flds.replace(' ','').split(',')
    213                 for i,fld in enumerate(flds):
    214                     if i < ncol-1:
    215                         text = wx.StaticText(self.panel,label='%s, '%(fld))
    216                         tableSizer.Add(text,0,WACV|wx.ALIGN_RIGHT)
    217                     else:
    218                         text = wx.StaticText(self.panel,label='%s '%(fld))
    219                         tableSizer.Add(text,0,WACV|wx.ALIGN_RIGHT)
    220                 text = wx.StaticText(self.panel,label=' (%s) '%(self.names[j]))
    221                 if self.spins[j+ic*len(self.table)] < 0:
    222                     text.SetForegroundColour('Red')
    223                 tableSizer.Add(text,0,WACV|wx.ALIGN_RIGHT)
    224                 if not j%2:
    225                     tableSizer.Add((20,0))
    226             mainSizer.Add(tableSizer,0,wx.ALIGN_CENTER)
    227            
    228         btnsizer = wx.StdDialogButtonSizer()
    229         OKbtn = wx.Button(self.panel, wx.ID_OK)
    230         OKbtn.SetDefault()
    231         btnsizer.AddButton(OKbtn)
    232         btnsizer.Realize()
    233         mainSizer.Add((0,10))
    234         mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
    235         self.panel.SetSizer(mainSizer)
    236         size = np.array(self.GetSize())
    237         self.panel.SetupScrolling()
    238         size = [size[0]-5,size[1]-20]       #this fiddling is needed for older wx!
    239         self.panel.SetSize(size)
    240         self.panel.SetAutoLayout(1)
    241 
    242     def Show(self):
    243         '''Use this method after creating the dialog to post it
    244         '''
    245         self.ShowModal()
    246         return   
    247 
    248 ################################################################################
    249 class SymOpDialog(wx.Dialog):
    250     '''Class to select a symmetry operator
    251     '''
    252     def __init__(self,parent,SGData,New=True,ForceUnit=False):
    253         wx.Dialog.__init__(self,parent,-1,'Select symmetry operator',
    254             pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
    255         panel = wx.Panel(self)
    256         self.SGData = SGData
    257         self.New = New
    258         self.Force = ForceUnit
    259         self.OpSelected = [0,0,0,[0,0,0],False,False]
    260         mainSizer = wx.BoxSizer(wx.VERTICAL)
    261         if ForceUnit:
    262             choice = ['No','Yes']
    263             self.force = wx.RadioBox(panel,-1,'Force to unit cell?',choices=choice)
    264             self.force.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
    265             mainSizer.Add(self.force,0,WACV|wx.TOP,5)
    266 #        if SGData['SGInv']:
    267         choice = ['No','Yes']
    268         self.inv = wx.RadioBox(panel,-1,'Choose inversion?',choices=choice)
    269         self.inv.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
    270         mainSizer.Add(self.inv,0,WACV)
    271         if SGData['SGLatt'] != 'P':
    272             LattOp = G2spc.Latt2text(SGData['SGLatt']).split(';')
    273             self.latt = wx.RadioBox(panel,-1,'Choose cell centering?',choices=LattOp)
    274             self.latt.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
    275             mainSizer.Add(self.latt,0,WACV)
    276         if SGData['SGLaue'] in ['-1','2/m','mmm','4/m','4/mmm']:
    277             Ncol = 2
    278         else:
    279             Ncol = 3
    280         OpList = []
    281         for Opr in SGData['SGOps']:
    282             OpList.append(G2spc.MT2text(Opr))
    283         self.oprs = wx.RadioBox(panel,-1,'Choose space group operator?',choices=OpList,
    284             majorDimension=Ncol)
    285         self.oprs.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
    286         mainSizer.Add(self.oprs,0,WACV|wx.BOTTOM,5)
    287         mainSizer.Add(wx.StaticText(panel,-1,"   Choose unit cell?"),0,WACV)
    288         cellSizer = wx.BoxSizer(wx.HORIZONTAL)
    289         cellName = ['X','Y','Z']
    290         self.cell = []
    291         for i in range(3):
    292             self.cell.append(wx.SpinCtrl(panel,-1,cellName[i],size=wx.Size(50,20)))
    293             self.cell[-1].SetRange(-3,3)
    294             self.cell[-1].SetValue(0)
    295             self.cell[-1].Bind(wx.EVT_SPINCTRL, self.OnOpSelect)
    296             cellSizer.Add(self.cell[-1],0,WACV)
    297         mainSizer.Add(cellSizer,0,WACV|wx.BOTTOM,5)
    298         if self.New:
    299             choice = ['No','Yes']
    300             self.new = wx.RadioBox(panel,-1,'Generate new positions?',choices=choice)
    301             self.new.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
    302             mainSizer.Add(self.new,0,WACV)
    303 
    304         OkBtn = wx.Button(panel,-1,"Ok")
    305         OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
    306         cancelBtn = wx.Button(panel,-1,"Cancel")
    307         cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
    308         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
    309         btnSizer.Add((20,20),1)
    310         btnSizer.Add(OkBtn)
    311         btnSizer.Add((20,20),1)
    312         btnSizer.Add(cancelBtn)
    313         btnSizer.Add((20,20),1)
    314 
    315         mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
    316         panel.SetSizer(mainSizer)
    317         panel.Fit()
    318         self.Fit()
    319 
    320     def OnOpSelect(self,event):
    321 #        if self.SGData['SGInv']:
    322         self.OpSelected[0] = self.inv.GetSelection()
    323         if self.SGData['SGLatt'] != 'P':
    324             self.OpSelected[1] = self.latt.GetSelection()
    325         self.OpSelected[2] = self.oprs.GetSelection()
    326         for i in range(3):
    327             self.OpSelected[3][i] = float(self.cell[i].GetValue())
    328         if self.New:
    329             self.OpSelected[4] = self.new.GetSelection()
    330         if self.Force:
    331             self.OpSelected[5] = self.force.GetSelection()
    332 
    333     def GetSelection(self):
    334         return self.OpSelected
    335 
    336     def OnOk(self,event):
    337         parent = self.GetParent()
    338         parent.Raise()
    339         self.EndModal(wx.ID_OK)
    340 
    341     def OnCancel(self,event):
    342         parent = self.GetParent()
    343         parent.Raise()
    344         self.EndModal(wx.ID_CANCEL)
    345        
    346 ################################################################################
    347 class SphereEnclosure(wx.Dialog):
    348     ''' Add atoms within sphere of enclosure to drawing
    349    
    350     :param wx.Frame parent: reference to parent frame (or None)
    351     :param general: general data (includes drawing data)
    352     :param atoms: drawing atoms data
    353     :param indx: list of selected atoms (may be empty)
    354    
    355     '''
    356     def __init__(self,parent,general,drawing,indx):
    357         wx.Dialog.__init__(self,parent,wx.ID_ANY,'Setup phase transformation',
    358             pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
    359         self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
    360         self.General = general
    361         self.Drawing = drawing
    362         self.indx = indx
    363         self.Sphere = [1.0,]
    364         self.centers = []
    365         self.atomTypes = [[item,True] for item in self.General['AtomTypes']]
    366        
    367         self.Draw()
    368        
    369     def Draw(self):
    370        
    371         def OnAtomType(event):
    372             Obj = event.GetEventObject()
    373             id = Ind[Obj.GetId()]
    374             self.atomTypes[id][1] = Obj.GetValue()
    375        
    376         self.panel.Destroy()
    377         self.panel = wx.Panel(self)
    378         mainSizer = wx.BoxSizer(wx.VERTICAL)
    379         mainSizer.Add(wx.StaticText(self.panel,label=' Sphere of enclosure controls:'),0,WACV)
    380         topSizer = wx.BoxSizer(wx.HORIZONTAL)
    381         atoms = []
    382         if len(self.indx):
    383             topSizer.Add(wx.StaticText(self.panel,label=' Sphere centered at atoms: '),0,WACV)
    384             cx,ct,cs = self.Drawing['atomPtrs'][:3]
    385             for id in self.indx:
    386                 atom = self.Drawing['Atoms'][id]
    387                 self.centers.append(atom[cx:cx+3])
    388                 atoms.append('%s(%s)'%(atom[ct-1],atom[cs-1]))
    389             topSizer.Add(wx.ComboBox(self.panel,choices=atoms,value=atoms[0],
    390                 style=wx.CB_READONLY|wx.CB_DROPDOWN),0,WACV)
    391         else:
    392             topSizer.Add(wx.StaticText(self.panel,label=' Sphere centered at drawing view point'),0,WACV)
    393             self.centers.append(self.Drawing['viewPoint'][0])
    394         mainSizer.Add(topSizer,0,WACV)
    395         sphereSizer = wx.BoxSizer(wx.HORIZONTAL)
    396         sphereSizer.Add(wx.StaticText(self.panel,label=' Sphere radius: '),0,WACV)
    397         radius = G2G.ValidatedTxtCtrl(self.panel,self.Sphere,0,nDig=(10,3),size=(65,25))
    398         sphereSizer.Add(radius,0,WACV)
    399         mainSizer.Add(sphereSizer,0,WACV)
    400         mainSizer.Add(wx.StaticText(self.panel,label=' Target selected atoms:'),0,WACV)
    401         atSizer = wx.BoxSizer(wx.HORIZONTAL)
    402         Ind = {}
    403         for i,item in enumerate(self.atomTypes):
    404             atm = wx.CheckBox(self.panel,label=item[0])
    405             atm.SetValue(item[1])
    406             atm.Bind(wx.EVT_CHECKBOX, OnAtomType)
    407             Ind[atm.GetId()] = i
    408             atSizer.Add(atm,0,WACV)
    409         mainSizer.Add(atSizer,0,WACV)
    410        
    411         OkBtn = wx.Button(self.panel,-1,"Ok")
    412         OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
    413         cancelBtn = wx.Button(self.panel,-1,"Cancel")
    414         cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
    415         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
    416         btnSizer.Add((20,20),1)
    417         btnSizer.Add(OkBtn)
    418         btnSizer.Add((20,20),1)
    419         btnSizer.Add(cancelBtn)
    420         btnSizer.Add((20,20),1)
    421        
    422         mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
    423         self.panel.SetSizer(mainSizer)
    424         self.panel.Fit()
    425         self.Fit()
    426        
    427     def GetSelection(self):
    428         used = []
    429         for atm in self.atomTypes:
    430             if atm[1]:
    431                 used.append(str(atm[0]))
    432         return self.centers,self.Sphere[0],used
    433 
    434     def OnOk(self,event):
    435         parent = self.GetParent()
    436         parent.Raise()
    437         self.EndModal(wx.ID_OK)
    438 
    439     def OnCancel(self,event):
    440         parent = self.GetParent()
    441         parent.Raise()
    442         self.EndModal(wx.ID_CANCEL)
    443        
    444 ################################################################################
    445 class TransformDialog(wx.Dialog):
    446     ''' Phase transformation
    447    
    448     :param wx.Frame parent: reference to parent frame (or None)
    449     :param phase: phase data
    450    
    451     #NB: commonNames & commonTrans defined at top of this file
    452     '''
    453     def __init__(self,parent,phase):
    454         wx.Dialog.__init__(self,parent,wx.ID_ANY,'Setup phase transformation',
    455             pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
    456         self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
    457         self.Phase = copy.deepcopy(phase)   #will be a new phase!
    458 #        self.Super = phase['General']['Super']
    459 #        if self.Super:
    460 #            self.Trans = np.eye(4)
    461 #            self.Vec = np.zeros(4)
    462 #        else:
    463         self.Trans = np.eye(3)
    464         self.Vec = np.zeros(3)
    465         self.oldSpGrp = phase['General']['SGData']['SpGrp']
    466         self.oldSGdata = phase['General']['SGData']
    467         self.newSpGrp = self.Phase['General']['SGData']['SpGrp']
    468         self.oldCell = phase['General']['Cell'][1:8]
    469         self.newCell = self.Phase['General']['Cell'][1:8]
    470         self.Common = 'abc'
    471         self.ifMag = False
    472         self.ifConstr = True
    473         self.Draw()
    474 
    475     def Draw(self):
    476                
    477         def OnCommon(event):
    478             Obj = event.GetEventObject()
    479             self.Common = Obj.GetValue()
    480             if '*' in self.Common:
    481                 A,B = G2lat.cell2AB(self.oldCell[:6])
    482                 self.newCell[2:5] = [A[2,2],90.,90.]
    483                 a,b = G2lat.cell2AB(self.newCell[:6])
    484                 self.Trans = np.inner(a.T,B)    #correct!
    485                 self.ifConstr = False
    486                 self.newSpGrp = 'P 1'
    487                 SGErr,SGData = G2spc.SpcGroup(self.newSpGrp)
    488                 self.Phase['General']['SGData'] = SGData
    489             else:
    490                 if self.Common == commonNames[-1]:      #change setting
    491                     self.Vec = G2spc.spg2origins[self.oldSpGrp]
    492                     self.newSpGrp = self.oldSpGrp
    493                 else:
    494                     self.Trans = commonTrans[self.Common]
    495                     if 'R' == self.Common[-1]:
    496                         self.newSpGrp += ' r'
    497                         SGErr,SGData = G2spc.SpcGroup(self.newSpGrp)
    498                         self.Phase['General']['SGData'] = SGData
    499                         SGTxt.SetValue(self.newSpGrp)
    500             OnTest(event)
    501        
    502         def OnSpaceGroup(event):
    503             event.Skip()
    504             Flds = SGTxt.GetValue().split()
    505             Flds[0] = Flds[0].upper()
    506             #get rid of extra spaces between fields first
    507             for fld in Flds: fld = fld.strip()
    508             SpcGp = ' '.join(Flds)
    509             if SpcGp == self.newSpGrp: #didn't change it!
    510                 return
    511             # try a lookup on the user-supplied name
    512             SpGrpNorm = G2spc.StandardizeSpcName(SpcGp)
    513             if SpGrpNorm:
    514                 SGErr,SGData = G2spc.SpcGroup(SpGrpNorm)
    515             else:
    516                 SGErr,SGData = G2spc.SpcGroup(SpcGp)
    517             if SGErr:
    518                 text = [G2spc.SGErrors(SGErr)+'\nSpace Group set to previous']
    519                 SGTxt.SetValue(self.newSpGrp)
    520                 msg = 'Space Group Error'
    521                 Style = wx.ICON_EXCLAMATION
    522                 Text = '\n'.join(text)
    523                 wx.MessageBox(Text,caption=msg,style=Style)
    524             else:
    525                 text,table = G2spc.SGPrint(SGData)
    526                 self.Phase['General']['SGData'] = SGData
    527                 self.newSpGrp = SpcGp
    528                 SGTxt.SetValue(self.Phase['General']['SGData']['SpGrp'])
    529                 msg = 'Space Group Information'
    530                 G2G.SGMessageBox(self.panel,msg,text,table).Show()
    531             if self.Phase['General']['Type'] == 'magnetic':
    532                 Nops = len(SGData['SGOps'])*len(SGData['SGCen'])
    533                 if SGData['SGInv']:
    534                     Nops *= 2
    535                 SGData['SpnFlp'] = Nops*[1,]
    536 #            if self.Phase['General']['Type'] in ['modulated',]:
    537 #                self.Phase['General']['SuperSg'] = SetDefaultSSsymbol()
    538 #                self.Phase['General']['SSGData'] = G2spc.SSpcGroup(generalData['SGData'],generalData['SuperSg'])[1]
    539 
    540         def OnTest(event):
    541             self.newCell = G2lat.TransformCell(self.oldCell[:6],self.Trans)
    542             wx.CallAfter(self.Draw)
    543            
    544         def OnMag(event):
    545             self.ifMag = mag.GetValue()
    546            
    547         def OnConstr(event):
    548             self.ifConstr = constr.GetValue()
    549 
    550         self.panel.Destroy()
    551         self.panel = wx.Panel(self)
    552         mainSizer = wx.BoxSizer(wx.VERTICAL)
    553         MatSizer = wx.BoxSizer(wx.HORIZONTAL)
    554         transSizer = wx.BoxSizer(wx.VERTICAL)
    555         transSizer.Add(wx.StaticText(self.panel,label=" XYZ Transformation matrix & vector: M*X+V = X'"))
    556 #        if self.Super:
    557 #            Trmat = wx.FlexGridSizer(4,4,0,0)
    558 #        else:
    559         commonSizer = wx.BoxSizer(wx.HORIZONTAL)
    560         commonSizer.Add(wx.StaticText(self.panel,label=' Common transformations: '),0,WACV)
    561         if self.oldSpGrp not in G2spc.spg2origins:
    562             common = wx.ComboBox(self.panel,value=self.Common,choices=commonNames[:-1],
    563                 style=wx.CB_READONLY|wx.CB_DROPDOWN)
    564         else:
    565             common = wx.ComboBox(self.panel,value=self.Common,choices=commonNames,
    566                 style=wx.CB_READONLY|wx.CB_DROPDOWN)
    567         common.Bind(wx.EVT_COMBOBOX,OnCommon)
    568         commonSizer.Add(common,0,WACV)
    569         transSizer.Add(commonSizer)
    570         Trmat = wx.FlexGridSizer(3,5,0,0)
    571         for iy,line in enumerate(self.Trans):
    572             for ix,val in enumerate(line):
    573                 item = G2G.ValidatedTxtCtrl(self.panel,self.Trans[iy],ix,nDig=(10,3),size=(65,25))
    574                 Trmat.Add(item)
    575             Trmat.Add((25,0),0)
    576             vec = G2G.ValidatedTxtCtrl(self.panel,self.Vec,iy,nDig=(10,3),size=(65,25))
    577             Trmat.Add(vec)
    578         transSizer.Add(Trmat)
    579         MatSizer.Add((10,0),0)
    580         MatSizer.Add(transSizer)
    581         mainSizer.Add(MatSizer)
    582         mainSizer.Add(wx.StaticText(self.panel,label=' Old lattice parameters:'),0,WACV)
    583         mainSizer.Add(wx.StaticText(self.panel,label=
    584             ' a = %.5f       b = %.5f      c = %.5f'%(self.oldCell[0],self.oldCell[1],self.oldCell[2])),0,WACV)
    585         mainSizer.Add(wx.StaticText(self.panel,label=' alpha = %.3f beta = %.3f gamma = %.3f'%
    586             (self.oldCell[3],self.oldCell[4],self.oldCell[5])),0,WACV)
    587         mainSizer.Add(wx.StaticText(self.panel,label=' volume = %.3f'%(self.oldCell[6])),0,WACV)
    588         mainSizer.Add(wx.StaticText(self.panel,label=' New lattice parameters:'),0,WACV)
    589         mainSizer.Add(wx.StaticText(self.panel,label=
    590             ' a = %.5f       b = %.5f      c = %.5f'%(self.newCell[0],self.newCell[1],self.newCell[2])),0,WACV)
    591         mainSizer.Add(wx.StaticText(self.panel,label=' alpha = %.3f beta = %.3f gamma = %.3f'%
    592             (self.newCell[3],self.newCell[4],self.newCell[5])),0,WACV)
    593         mainSizer.Add(wx.StaticText(self.panel,label=' volume = %.3f'%(self.newCell[6])),0,WACV)
    594         sgSizer = wx.BoxSizer(wx.HORIZONTAL)
    595         sgSizer.Add(wx.StaticText(self.panel,label='  Space group: '),0,WACV)
    596         SGTxt = wx.TextCtrl(self.panel,value=self.newSpGrp,style=wx.TE_PROCESS_ENTER)
    597         SGTxt.Bind(wx.EVT_TEXT_ENTER,OnSpaceGroup)
    598         SGTxt.Bind(wx.EVT_KILL_FOCUS,OnSpaceGroup)
    599         sgSizer.Add(SGTxt,0,WACV)
    600         mainSizer.Add(sgSizer,0,WACV)
    601         if 'magnetic' not in self.Phase['General']['Type']:
    602             mag = wx.CheckBox(self.panel,label=' Make new phase magnetic?')
    603             mag.Bind(wx.EVT_CHECKBOX,OnMag)
    604             mainSizer.Add(mag,0,WACV)
    605             mainSizer.Add(wx.StaticText(self.panel, \
    606                 label=' NB: Nonmagnetic atoms will be deleted from new phase'),0,WACV)
    607             constr = wx.CheckBox(self.panel,label=' Make constraints between phases?')
    608             mainSizer.Add(wx.StaticText(self.panel, \
    609                 label=' Constraints not correct for non-diagonal transforms'),0,WACV)
    610             constr.SetValue(self.ifConstr)
    611             constr.Bind(wx.EVT_CHECKBOX,OnConstr)
    612             mainSizer.Add(constr,0,WACV)
    613 
    614         TestBtn = wx.Button(self.panel,-1,"Test")
    615         TestBtn.Bind(wx.EVT_BUTTON, OnTest)
    616         OkBtn = wx.Button(self.panel,-1,"Ok")
    617         OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
    618         cancelBtn = wx.Button(self.panel,-1,"Cancel")
    619         cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
    620         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
    621         btnSizer.Add((20,20),1)
    622         btnSizer.Add(TestBtn)
    623         btnSizer.Add((20,20),1)
    624         btnSizer.Add(OkBtn)
    625         btnSizer.Add((20,20),1)
    626         btnSizer.Add(cancelBtn)
    627         btnSizer.Add((20,20),1)
    628        
    629         mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
    630         self.panel.SetSizer(mainSizer)
    631         self.panel.Fit()
    632         self.Fit()
    633        
    634     def GetSelection(self):
    635         if self.ifMag:
    636             self.Phase['General']['Name'] += ' mag'
    637         else:
    638             self.Phase['General']['Name'] += ' %s'%(self.Common)
    639         self.Phase['General']['Cell'][1:] = G2lat.TransformCell(self.oldCell[:6],self.Trans)           
    640         return self.Phase,self.Trans,self.Vec,self.ifMag,self.ifConstr,self.Common
    641 
    642     def OnOk(self,event):
    643         parent = self.GetParent()
    644         parent.Raise()
    645         self.EndModal(wx.ID_OK)
    646 
    647     def OnCancel(self,event):
    648         parent = self.GetParent()
    649         parent.Raise()
    650         self.EndModal(wx.ID_CANCEL)
    651 ################################################################################
    652 class UseMagAtomDialog(wx.Dialog):
    653     '''Get user selected magnetic atoms after cell transformation
    654     '''
    655     def __init__(self,parent,Atoms,atCodes):
    656         wx.Dialog.__init__(self,parent,wx.ID_ANY,'Magnetic atom selection',
    657             pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
    658         self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
    659         self.Atoms = Atoms
    660         self.atCodes = atCodes
    661         self.Use = len(self.Atoms)*[True,]
    662         self.Draw()
    663        
    664     def Draw(self):
    665        
    666         def OnUseChk(event):
    667             Obj = event.GetEventObject()
    668             iuse = Indx[Obj.GetId()]
    669             self.Use[iuse] = not self.Use[iuse]
    670             Obj.SetValue(self.Use[iuse])
    671        
    672         self.panel.Destroy()
    673         self.panel = wx.Panel(self)
    674         Indx = {}
    675         mainSizer = wx.BoxSizer(wx.VERTICAL)
    676        
    677         mainSizer.Add(wx.StaticText(self.panel,label=' Name, x, y, z:'),0,WACV)
    678         atmSizer = wx.FlexGridSizer(0,2,5,5)
    679         for iuse,[use,atom] in enumerate(zip(self.Use,self.Atoms)):
    680             useChk = wx.CheckBox(self.panel,label='Use?')
    681             Indx[useChk.GetId()] = iuse
    682             useChk.SetValue(use)
    683             useChk.Bind(wx.EVT_CHECKBOX, OnUseChk)
    684             atmSizer.Add(useChk,0,WACV)
    685             text = ' %s %10.5f %10.5f %10.5f'%(atom[0],atom[3],atom[4],atom[5])
    686             atmSizer.Add(wx.StaticText(self.panel,label=text),0,WACV)
    687         mainSizer.Add(atmSizer)
    688        
    689         OkBtn = wx.Button(self.panel,-1,"Ok")
    690         OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
    691         cancelBtn = wx.Button(self.panel,-1,"Use All")
    692         cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
    693         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
    694         btnSizer.Add((20,20),1)
    695         btnSizer.Add(OkBtn)
    696         btnSizer.Add((20,20),1)
    697         btnSizer.Add(cancelBtn)
    698         btnSizer.Add((20,20),1)
    699        
    700         mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
    701         self.panel.SetSizer(mainSizer)
    702         self.panel.Fit()
    703         self.Fit()
    704        
    705     def GetSelection(self):
    706         useAtoms = []
    707         useatCodes = []
    708         for use,atom,code in zip(self.Use,self.Atoms,self.atCodes):
    709             if use:
    710                 useAtoms.append(atom)
    711                 useatCodes.append(code)
    712         return useAtoms,useatCodes
    713 
    714     def OnOk(self,event):
    715         parent = self.GetParent()
    716         parent.Raise()
    717         self.EndModal(wx.ID_OK)
    718 
    719     def OnCancel(self,event):
    720         parent = self.GetParent()
    721         parent.Raise()
    722         self.EndModal(wx.ID_CANCEL)
    723            
    724                
    725 ################################################################################
    726 class RotationDialog(wx.Dialog):
    727     ''' Get Rotate & translate matrix & vector - currently not used
    728     needs rethinking - possible use to rotate a group of atoms about some
    729     vector/origin + translation
    730    
    731     '''
    732     def __init__(self,parent):
    733         wx.Dialog.__init__(self,parent,wx.ID_ANY,'Atom group rotation/translation',
    734             pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
    735         self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
    736         self.Trans = np.eye(3)
    737         self.Vec = np.zeros(3)
    738         self.rotAngle = 0.
    739         self.rotVec = np.array([0.,0.,1.])
    740         self.Expand = ''
    741         self.Draw()
    742 
    743     def Draw(self):
    744 
    745         def OnExpand(event):
    746             self.Expand = expand.GetValue()
    747            
    748         def OnRotAngle(event):
    749             event.Skip()
    750             self.rotAngle = float(rotangle.GetValue())
    751             rotangle.SetValue('%5.3f'%(self.rotAngle))
    752             Q = G2mth.AVdeg2Q(self.rotAngle,self.rotVec)
    753             self.Trans = G2mth.Q2Mat(Q)
    754             self.Draw()
    755            
    756         def OnRotVec(event):
    757             event.Skip()
    758             vals = rotvec.GetValue()
    759             vals = vals.split()
    760             self.rotVec = np.array([float(val) for val in vals])
    761             rotvec.SetValue('%5.3f %5.3f %5.3f'%(self.rotVec[0],self.rotVec[1],self.rotVec[2]))
    762             Q = G2mth.AVdeg2Q(self.rotAngle,self.rotVec)
    763             self.Trans = G2mth.Q2Mat(Q)
    764             self.Draw()
    765            
    766         self.panel.Destroy()
    767         self.panel = wx.Panel(self)
    768         mainSizer = wx.BoxSizer(wx.VERTICAL)
    769         MatSizer = wx.BoxSizer(wx.HORIZONTAL)
    770         transSizer = wx.BoxSizer(wx.VERTICAL)
    771         transSizer.Add(wx.StaticText(self.panel,label=" XYZ Transformation matrix && vector: "+ \
    772             "\n B*M*A*(X-V)+V = X'\n A,B: Cartesian transformation matrices"))
    773         Trmat = wx.FlexGridSizer(3,5,0,0)
    774         for iy,line in enumerate(self.Trans):
    775             for ix,val in enumerate(line):
    776                 item = G2G.ValidatedTxtCtrl(self.panel,self.Trans[iy],ix,nDig=(10,3),size=(65,25))
    777                 Trmat.Add(item)
    778             Trmat.Add((25,0),0)
    779             vec = G2G.ValidatedTxtCtrl(self.panel,self.Vec,iy,nDig=(10,3),size=(65,25))
    780             Trmat.Add(vec)
    781         transSizer.Add(Trmat)
    782         MatSizer.Add((10,0),0)
    783         MatSizer.Add(transSizer)
    784         mainSizer.Add(MatSizer)
    785         rotationBox = wx.BoxSizer(wx.HORIZONTAL)
    786         rotationBox.Add(wx.StaticText(self.panel,label=' Rotation angle: '),0,WACV)
    787         rotangle = wx.TextCtrl(self.panel,value='%5.3f'%(self.rotAngle),
    788             size=(50,25),style=wx.TE_PROCESS_ENTER)
    789         rotangle.Bind(wx.EVT_TEXT_ENTER,OnRotAngle)
    790         rotangle.Bind(wx.EVT_KILL_FOCUS,OnRotAngle)
    791         rotationBox.Add(rotangle,0,WACV)
    792         rotationBox.Add(wx.StaticText(self.panel,label=' about vector: '),0,WACV)
    793         rotvec = wx.TextCtrl(self.panel,value='%5.3f %5.3f %5.3f'%(self.rotVec[0],self.rotVec[1],self.rotVec[2]),
    794             size=(100,25),style=wx.TE_PROCESS_ENTER)
    795         rotvec.Bind(wx.EVT_TEXT_ENTER,OnRotVec)
    796         rotvec.Bind(wx.EVT_KILL_FOCUS,OnRotVec)
    797         rotationBox.Add(rotvec,0,WACV)
    798         mainSizer.Add(rotationBox,0,WACV)
    799         expandChoice = ['','xy','xz','yz','xyz']
    800         expandBox = wx.BoxSizer(wx.HORIZONTAL)
    801         expandBox.Add(wx.StaticText(self.panel,label=' Expand -1 to +1 on: '),0,WACV)
    802         expand = wx.ComboBox(self.panel,value=self.Expand,choices=expandChoice,
    803             style=wx.CB_READONLY|wx.CB_DROPDOWN)
    804         expand.Bind(wx.EVT_COMBOBOX,OnExpand)
    805         expandBox.Add(expand,0,WACV)
    806         expandBox.Add(wx.StaticText(self.panel,label=' and find unique atoms '),0,WACV)       
    807         mainSizer.Add(expandBox)
    808                
    809         OkBtn = wx.Button(self.panel,-1,"Ok")
    810         OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
    811         cancelBtn = wx.Button(self.panel,-1,"Cancel")
    812         cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
    813         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
    814         btnSizer.Add((20,20),1)
    815         btnSizer.Add(OkBtn)
    816         btnSizer.Add((20,20),1)
    817         btnSizer.Add(cancelBtn)
    818         btnSizer.Add((20,20),1)
    819        
    820         mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
    821         self.panel.SetSizer(mainSizer)
    822         self.panel.Fit()
    823         self.Fit()
    824 
    825     def GetSelection(self):
    826         return self.Trans,self.Vec,self.Expand
    827 
    828     def OnOk(self,event):
    829         parent = self.GetParent()
    830         parent.Raise()
    831         self.EndModal(wx.ID_OK)
    832 
    833     def OnCancel(self,event):
    834         parent = self.GetParent()
    835         parent.Raise()
    836         self.EndModal(wx.ID_CANCEL)   
    837        
    838 ################################################################################
    839 class DIFFaXcontrols(wx.Dialog):
    840     ''' Solicit items needed to prepare DIFFaX control.dif file
    841     '''
    842     def __init__(self,parent,ctrls,parms=None):
    843         wx.Dialog.__init__(self,parent,wx.ID_ANY,'DIFFaX controls',
    844             pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
    845         self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
    846         self.ctrls = ctrls
    847         self.calcType = 'powder pattern'
    848         self.plane = 'h0l'
    849         self.planeChoice = ['h0l','0kl','hhl','h-hl',]
    850         self.lmax = '2'
    851         self.lmaxChoice = [str(i+1) for i in range(6)]
    852         self.Parms = parms
    853         self.Parm = None
    854         if self.Parms != None:
    855             self.Parm = self.Parms[0]
    856         self.parmRange = [0.,1.]
    857         self.parmStep = 2
    858         self.Inst = 'Gaussian'
    859         self.Draw()
    860        
    861     def Draw(self):
    862        
    863         def OnCalcType(event):
    864             self.calcType = calcType.GetValue()
    865             wx.CallAfter(self.Draw)
    866            
    867         def OnPlane(event):
    868             self.plane = plane.GetValue()
    869            
    870         def OnMaxL(event):
    871             self.lmax = lmax.GetValue()
    872            
    873         def OnParmSel(event):
    874             self.Parm = parmsel.GetValue()
    875            
    876         def OnNumStep(event):
    877             self.parmStep = int(numStep.GetValue())
    878            
    879         def OnParmRange(event):
    880             event.Skip()
    881             vals = parmrange.GetValue().split()
    882             try:
    883                 vals = [float(vals[0]),float(vals[1])]
    884             except ValueError:
    885                 vals = self.parmRange
    886             parmrange.SetValue('%.3f %.3f'%(vals[0],vals[1]))
    887             self.parmRange = vals
    888            
    889         def OnInstSel(event):
    890             self.Inst = instsel.GetValue()
    891        
    892         self.panel.Destroy()
    893         self.panel = wx.Panel(self)
    894         mainSizer = wx.BoxSizer(wx.VERTICAL)
    895         mainSizer.Add(wx.StaticText(self.panel,label=' Controls for DIFFaX'),0,WACV)
    896         if self.Parms:
    897             mainSizer.Add(wx.StaticText(self.panel,label=' Sequential powder pattern simulation'),0,WACV)
    898         else:
    899             calcChoice = ['powder pattern','selected area']
    900             calcSizer = wx.BoxSizer(wx.HORIZONTAL)
    901             calcSizer.Add(wx.StaticText(self.panel,label=' Select calculation type: '),0,WACV)
    902             calcType = wx.ComboBox(self.panel,value=self.calcType,choices=calcChoice,
    903                 style=wx.CB_READONLY|wx.CB_DROPDOWN)
    904             calcType.Bind(wx.EVT_COMBOBOX,OnCalcType)
    905             calcSizer.Add(calcType,0,WACV)
    906             mainSizer.Add(calcSizer)
    907         if self.Parms:
    908             parmSel = wx.BoxSizer(wx.HORIZONTAL)
    909             parmSel.Add(wx.StaticText(self.panel,label=' Select parameter to vary: '),0,WACV)
    910             parmsel = wx.ComboBox(self.panel,value=self.Parm,choices=self.Parms,
    911                 style=wx.CB_READONLY|wx.CB_DROPDOWN)
    912             parmsel.Bind(wx.EVT_COMBOBOX,OnParmSel)
    913             parmSel.Add(parmsel,0,WACV)
    914             mainSizer.Add(parmSel)
    915             mainSizer.Add(wx.StaticText(self.panel,label=' Enter parameter range & no. steps: '),0,WACV)
    916             parmRange =  wx.BoxSizer(wx.HORIZONTAL)
    917             numChoice = [str(i+1) for i in range(10)]
    918             parmrange = wx.TextCtrl(self.panel,value='%.3f %.3f'%(self.parmRange[0],self.parmRange[1]),
    919                 style=wx.TE_PROCESS_ENTER)
    920             parmrange.Bind(wx.EVT_TEXT_ENTER,OnParmRange)
    921             parmrange.Bind(wx.EVT_KILL_FOCUS,OnParmRange)
    922             parmRange.Add(parmrange,0,WACV)
    923             numStep = wx.ComboBox(self.panel,value=str(self.parmStep),choices=numChoice,
    924                 style=wx.CB_READONLY|wx.CB_DROPDOWN)
    925             numStep.Bind(wx.EVT_COMBOBOX,OnNumStep)
    926             parmRange.Add(numStep,0,WACV)
    927             mainSizer.Add(parmRange)           
    928         if 'selected' in self.calcType:
    929             planeSizer = wx.BoxSizer(wx.HORIZONTAL)
    930             planeSizer.Add(wx.StaticText(self.panel,label=' Select plane: '),0,WACV)
    931             plane = wx.ComboBox(self.panel,value=self.plane,choices=self.planeChoice,
    932                 style=wx.CB_READONLY|wx.CB_DROPDOWN)
    933             plane.Bind(wx.EVT_COMBOBOX,OnPlane)
    934             planeSizer.Add(plane,0,WACV)
    935             planeSizer.Add(wx.StaticText(self.panel,label=' Max. l index: '),0,WACV)
    936             lmax = wx.ComboBox(self.panel,value=self.lmax,choices=self.lmaxChoice,
    937                 style=wx.CB_READONLY|wx.CB_DROPDOWN)
    938             lmax.Bind(wx.EVT_COMBOBOX,OnMaxL)
    939             planeSizer.Add(lmax,0,WACV)           
    940             mainSizer.Add(planeSizer)
    941         else:
    942             instChoice = ['None','Mean Gaussian','Gaussian',]
    943             instSizer = wx.BoxSizer(wx.HORIZONTAL)
    944             instSizer.Add(wx.StaticText(self.panel,label=' Select instrument broadening: '),0,WACV)
    945             instsel = wx.ComboBox(self.panel,value=self.Inst,choices=instChoice,
    946                 style=wx.CB_READONLY|wx.CB_DROPDOWN)
    947             instsel.Bind(wx.EVT_COMBOBOX,OnInstSel)
    948             instSizer.Add(instsel,0,WACV)
    949             mainSizer.Add(instSizer)
    950         OkBtn = wx.Button(self.panel,-1,"Ok")
    951         OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
    952         cancelBtn = wx.Button(self.panel,-1,"Cancel")
    953         cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
    954         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
    955         btnSizer.Add((20,20),1)
    956         btnSizer.Add(OkBtn)
    957         btnSizer.Add((20,20),1)
    958         btnSizer.Add(cancelBtn)
    959         btnSizer.Add((20,20),1)
    960        
    961         mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
    962         self.panel.SetSizer(mainSizer)
    963         self.panel.Fit()
    964         self.Fit()
    965        
    966     def GetSelection(self):
    967         if 'powder' in self.calcType:
    968             return 'PWDR',self.Inst,self.Parm,self.parmRange,self.parmStep
    969         elif 'selected' in self.calcType:
    970             return 'SADP',self.plane,self.lmax
    971 
    972     def OnOk(self,event):
    973         parent = self.GetParent()
    974         parent.Raise()
    975         self.EndModal(wx.ID_OK)
    976 
    977     def OnCancel(self,event):
    978         parent = self.GetParent()
    979         parent.Raise()
    980         self.EndModal(wx.ID_CANCEL)
    981            
    982        
    983 ################################################################################
    984197class MergeDialog(wx.Dialog):
    985198    ''' HKL transformation & merge dialog
     
    1116329        parent.Raise()
    1117330        self.EndModal(wx.ID_CANCEL)
    1118 
    1119        
     331###############################################################################
     332# Main application
     333###############################################################################
     334def GSASIImain():
     335    '''Start up the GSAS-II application'''
     336    #application = GSASIImain() # don't redirect output, someday we
     337    # may want to do this if we can
     338    application = GSASIIGUI(0)
     339    if GSASIIpath.GetConfigValue('wxInspector'):
     340        import wx.lib.inspection as wxeye
     341        wxeye.InspectionTool().Show()
     342
     343    #application.main.OnRefine(None)
     344    application.SetAppDisplayName('GSAS-II')
     345    #application.GetTopWindow().dataWindow.SendSizeEvent()
     346    #application.GetTopWindow().treePanel.SendSizeEvent()
     347    #application.GetTopWindow().SendSizeEvent()
     348    application.GetTopWindow().Show(True)
     349    application.MainLoop()
     350
     351class GSASIIGUI(wx.App):
     352    '''Defines a wxApp for GSAS-II
     353
     354    Creates a wx frame (self.main) which contains the display of the
     355    data tree.
     356    '''
     357    def OnInit(self):
     358        '''Called automatically when the app is created.'''
     359        import platform
     360        if '2.7' not in sys.version[:5]:
     361            dlg = wx.MessageDialog(None,
     362                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
     363                'Python version error',  wx.OK)
     364            try:
     365                dlg.ShowModal()
     366            finally:
     367                dlg.Destroy()
     368            sys.exit()
     369        self.main = GSASII(None)
     370        self.SetTopWindow(self.main)
     371        # save the current package versions
     372        self.main.PackageVersions = []
     373        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
     374        for p in (wx,mpl,np,sp,ogl):
     375            self.main.PackageVersions.append([p.__name__,p.__version__])
     376        try:
     377            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
     378        except:
     379            try:
     380                from PIL import PILLOW_VERSION
     381                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
     382            except:
     383                pass
     384        self.main.PackageVersions.append(['Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
     385       
     386        return True
     387    # def MacOpenFile(self, filename):
     388    #     '''Called on Mac every time a file is dropped on the app when it is running,
     389    #     treat this like a File/Open project menu action.
     390    #     Should be ignored on other platforms
     391    #     '''
     392    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
     393    #     print 'MacOpen',filename
     394    #     if os.path.splitext(filename)[1] == '.py': return
     395    #     # end PATCH
     396    #     self.main.OnFileOpen(None,filename)
     397    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
     398
    1120399################################################################################
    1121 class AddHatomDialog(wx.Dialog):
    1122     '''H atom addition dialog. After :meth:`ShowModal` returns, the results
    1123     are found in dict :attr:`self.data`, which is accessed using :meth:`GetData`.
     400# Create main frame (window) for GUI
     401################################################################################
     402class GSASII(wx.Frame):
     403    '''Define the main GSAS-II frame and its associated menu items
     404    '''
     405    def MenuBinding(self,event):
     406        '''Called when a menu is clicked upon; looks up the binding in table
     407        '''
     408        log.InvokeMenuCommand(event.GetId(),self,event)
     409           
     410    def Bind(self,eventtype,handler,*args,**kwargs):
     411        '''Override the Bind function so that we can wrap calls that will be logged.
     412       
     413        N.B. This is a bit kludgy. Menu bindings with an id are wrapped and
     414        menu bindings with an object and no id are not.
     415        '''
     416        if eventtype == wx.EVT_MENU and 'id' in kwargs:
     417            menulabels = log.SaveMenuCommand(kwargs['id'],self,handler)
     418            if menulabels:
     419                wx.Frame.Bind(self,eventtype,self.MenuBinding,*args,**kwargs)
     420                return
     421        wx.Frame.Bind(self,eventtype,handler,*args,**kwargs)     
    1124422   
    1125     :param wx.Frame parent: reference to parent frame (or None)
    1126     :param dict Neigh: a dict of atom names with list of atom name, dist pairs for neighboring atoms
    1127     :param dict phase: a dict containing the phase as defined by
    1128       :ref:`Phase Tree Item <Phase_table>`   
    1129     '''
    1130     def __init__(self,parent,Neigh,phase):
    1131         wx.Dialog.__init__(self,parent,wx.ID_ANY,'H atom add',
    1132             pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
    1133         self.panel = wxscroll.ScrolledPanel(self)         #just a dummy - gets destroyed in Draw!
    1134         self.Neigh = Neigh
    1135         self.phase = phase
    1136         self.Hatoms = []
    1137         self.Draw(self.Neigh,self.phase)
     423    def _Add_FileMenuItems(self, parent):
     424        item = parent.Append(
     425            help='Open a GSAS-II project file (*.gpx)', id=wx.ID_ANY,
     426            kind=wx.ITEM_NORMAL,text='&Open project...')
     427        self.Bind(wx.EVT_MENU, self.OnFileOpen, id=item.GetId())
     428        item = parent.Append(
     429            help='Save project under current name', id=wx.ID_ANY,
     430            kind=wx.ITEM_NORMAL,text='&Save project')
     431        self.Bind(wx.EVT_MENU, self.OnFileSave, id=item.GetId())
     432        item = parent.Append(
     433            help='Save current project to new file', id=wx.ID_ANY,
     434            kind=wx.ITEM_NORMAL,text='Save project as...')
     435        self.Bind(wx.EVT_MENU, self.OnFileSaveas, id=item.GetId())
     436        item = parent.Append(
     437            help='Create empty new project, saving current is optional', id=wx.ID_ANY,
     438            kind=wx.ITEM_NORMAL,text='&New project')
     439        self.Bind(wx.EVT_MENU, self.OnFileClose, id=item.GetId())
     440        item = parent.Append(wx.ID_PREFERENCES, text = "&Preferences")
     441        self.Bind(wx.EVT_MENU, self.OnPreferences, item)
     442        if GSASIIpath.GetConfigValue('debug'):
     443            def OnIPython(event):
     444                GSASIIpath.IPyBreak()
     445            item = parent.Append(wx.ID_ANY, text = "IPython Console")
     446            self.Bind(wx.EVT_MENU, OnIPython, item)
     447        item = parent.Append(
     448            help='Exit from GSAS-II', id=wx.ID_ANY,
     449            kind=wx.ITEM_NORMAL,text='&Exit')
     450        self.Bind(wx.EVT_MENU, self.OnFileExit, id=item.GetId())
     451       
     452    def _Add_DataMenuItems(self,parent):
     453        # item = parent.Append(
     454        #     help='',id=wx.ID_ANY,
     455        #     kind=wx.ITEM_NORMAL,
     456        #     text='Read image data...')
     457        # self.Bind(wx.EVT_MENU, self.OnImageRead, id=item.GetId())
     458        item = parent.Append(
     459            help='',id=wx.ID_ANY,
     460            kind=wx.ITEM_NORMAL,
     461            text='Read Powder Pattern Peaks...')
     462        self.Bind(wx.EVT_MENU, self.OnReadPowderPeaks, id=item.GetId())
     463        item = parent.Append(
     464            help='',id=wx.ID_ANY,
     465            kind=wx.ITEM_NORMAL,
     466            text='Sum powder data')
     467        self.Bind(wx.EVT_MENU, self.OnPwdrSum, id=item.GetId())
     468        item = parent.Append(
     469            help='',id=wx.ID_ANY,
     470            kind=wx.ITEM_NORMAL,
     471            text='Sum image data')
     472        self.Bind(wx.EVT_MENU, self.OnImageSum, id=item.GetId())
     473        item = parent.Append(
     474            help='',id=wx.ID_ANY,
     475            kind=wx.ITEM_NORMAL,
     476            text='Add new phase')
     477        self.Bind(wx.EVT_MENU, self.OnAddPhase, id=item.GetId())
     478        item = parent.Append(
     479            help='',id=wx.ID_ANY,
     480            kind=wx.ITEM_NORMAL,
     481            text='Delete phase')
     482        self.Bind(wx.EVT_MENU, self.OnDeletePhase, id=item.GetId())
     483        item = parent.Append(
     484            help='Rename the selected data tree item (PWDR, HKLF or IMG)',id=wx.ID_ANY,
     485            kind=wx.ITEM_NORMAL,
     486            text='Rename tree item')
     487        self.Bind(wx.EVT_MENU, self.OnRenameData, id=item.GetId())
     488        item = parent.Append(
     489            help='Delete selected data items from data tree',id=wx.ID_ANY,
     490            kind=wx.ITEM_NORMAL,
     491            text='Delete tree items')
     492        self.Bind(wx.EVT_MENU, self.OnDataDelete, id=item.GetId())
     493        expandmenu = wx.Menu()
     494        item = parent.AppendMenu(
     495            wx.ID_ANY, 'Expand tree items', expandmenu,
     496            help='Expand items of type in GSAS-II data tree')
     497        for s in 'all','IMG','PWDR','PDF','HKLF','SASD','REFD':
     498            if s == 'all':
     499                help = 'Expand all items in GSAS-II data tree'
     500            else:
     501                help = 'Expand '+s+' type items in GSAS-II data tree'
     502            item = expandmenu.Append(wx.ID_ANY,kind=wx.ITEM_NORMAL,text=s,help=help)
     503            self.Bind(wx.EVT_MENU,self.ExpandAll,id=item.GetId())
     504        movemenu = wx.Menu()
     505        item = parent.AppendMenu(
     506            wx.ID_ANY, 'Move tree items', movemenu,
     507            help='Move items of type items to end of GSAS-II data tree')
     508        for s in 'IMG','PWDR','PDF','HKLF','SASD','REFD','Phase':
     509            help = 'Move '+s+' type items to end of GSAS-II data tree'
     510            item = movemenu.Append(wx.ID_ANY,kind=wx.ITEM_NORMAL,text=s,help=help)
     511            self.Bind(wx.EVT_MENU,self.MoveTreeItems,id=item.GetId())
     512
     513    def _Add_CalculateMenuItems(self,parent):
     514        item = parent.Append(help='Create PDF tree entries for selected powder patterns',
     515            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='Setup PDFs')
     516        self.MakePDF.append(item)
     517        self.Bind(wx.EVT_MENU, self.OnMakePDFs, id=item.GetId())
     518       
     519        item = parent.Append(help='View least squares parameters',
     520            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='&View LS parms')
     521        self.Bind(wx.EVT_MENU, self.OnShowLSParms, id=item.GetId())
     522       
     523        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
     524            text='&Refine')
     525        if len(self.Refine): # extend state for new menus to match main (on mac)
     526            state = self.Refine[0].IsEnabled()
     527        else:
     528            state = False
     529        item.Enable(state)
     530        self.Refine.append(item)
     531        self.Bind(wx.EVT_MENU, self.OnRefine, id=item.GetId())
     532       
     533        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
     534            text='Sequential refine')
     535        self.Bind(wx.EVT_MENU, self.OnSeqRefine, id=item.GetId())
     536        if len(self.SeqRefine): # extend state for new menus to match main (on mac)
     537            state = self.SeqRefine[0].IsEnabled()
     538        else:
     539            state = False
     540        item.Enable(state)
     541        self.SeqRefine.append(item) # save menu obj for use in self.EnableSeqRefineMenu
     542#        if GSASIIpath.GetConfigValue('debug'): # allow exceptions for debugging
     543#            item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
     544#                text='tree test')
     545#            self.Bind(wx.EVT_MENU, self.TreeTest, id=item.GetId())
     546
     547    def _init_Imports(self):
     548        '''import all the G2phase*.py & G2sfact*.py & G2pwd*.py files that
     549        are found in the path
     550        '''
     551
     552        self.ImportPhaseReaderlist = []
     553        self._init_Import_routines('phase',self.ImportPhaseReaderlist,'Phase')
     554        self.ImportSfactReaderlist = []
     555        self._init_Import_routines('sfact',self.ImportSfactReaderlist,'Struct_Factor')
     556        self.ImportPowderReaderlist = []
     557        self._init_Import_routines('pwd',self.ImportPowderReaderlist,'Powder_Data')
     558        self.ImportSmallAngleReaderlist = []
     559        self._init_Import_routines('sad',self.ImportSmallAngleReaderlist,'SmallAngle_Data')
     560        self.ImportReflectometryReaderlist = []
     561        self._init_Import_routines('rfd',self.ImportReflectometryReaderlist,'Reflectometry_Data')
     562        self.ImportPDFReaderlist = []
     563        self._init_Import_routines('pdf',self.ImportPDFReaderlist,'PDF_Data')
     564        self.ImportImageReaderlist = []
     565        self._init_Import_routines('img',self.ImportImageReaderlist,'Images')
     566        self.ImportMenuId = {}
     567
     568    def _init_Import_routines(self,prefix,readerlist,errprefix):
     569        '''import all the import readers matching the prefix
     570        '''
     571        #path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file
     572        #pathlist = sys.path[:]
     573        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
     574        #path2GSAS2 = os.path.join(
     575        #    os.path.dirname(os.path.realpath(__file__)), # location of this file
     576        #    'imports')
     577        pathlist = sys.path[:]
     578        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
     579        if '.' not in pathlist: pathlist.append('.') # insert the directory where G2 is started
     580
     581        filelist = []
     582        for path in pathlist:
     583            for filename in glob.iglob(os.path.join(
     584                path,
     585                "G2"+prefix+"*.py")):
     586                filelist.append(filename)   
     587                #print 'debug: found',filename
     588        filelist = sorted(list(set(filelist))) # remove duplicates
     589        for filename in filelist:
     590            path,rootname = os.path.split(filename)
     591            pkg = os.path.splitext(rootname)[0]
     592            try:
     593                fp = None
     594                fp, fppath,desc = imp.find_module(pkg,[path,])
     595                pkg = imp.load_module(pkg,fp,fppath,desc)
     596                for clss in inspect.getmembers(pkg): # find classes defined in package
     597                    if clss[0].startswith('_'): continue
     598                    if inspect.isclass(clss[1]):
     599                        # check if we have the required methods
     600                        for m in 'Reader','ExtensionValidator','ContentsValidator':
     601                            if not hasattr(clss[1],m): break
     602                            if not callable(getattr(clss[1],m)): break
     603                        else:
     604                            reader = clss[1]() # create an import instance
     605                            if reader.UseReader:
     606                                readerlist.append(reader)
     607            except AttributeError:
     608                print 'Import_'+errprefix+': Attribute Error '+ filename
     609            #except ImportError:
     610            #    print 'Import_'+errprefix+': Error importing file '+ filename
     611            except Exception,errmsg:
     612                print('\nImport_'+errprefix+': Error importing file '+ filename)
     613                print(u'Error message: {}\n'.format(errmsg))
     614            if fp: fp.close()
     615
     616    def EnableSeqRefineMenu(self):
     617        '''Enable or disable the sequential refinement menu items based on the
     618        contents of the Controls 'Seq Data' item (if present)
     619        '''
     620        controls = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,self.root, 'Controls'))
     621        if controls.get('Seq Data'):
     622            for i in self.SeqRefine: i.Enable(True)
     623        else:
     624            for i in self.SeqRefine: i.Enable(False)
     625
     626    def PreviewFile(self,filename,fp):
     627        'confirm we have the right file'
     628        rdmsg = 'File '+ filename +' begins:\n\n'
     629        try:
     630            rdmsg += fp.read(80)
     631            rdmsg += '\n\nDo you want to read this file?'
     632        except UnicodeDecodeError:
     633            rdmsg = None
     634        if rdmsg is None or not all([ord(c) < 128 and ord(c) != 0 for c in rdmsg]): # show only if ASCII
     635            rdmsg = 'File '+ filename +' is a binary file. Do you want to read this file?'
     636        # it would be better to use something that
     637        # would resize better, but this will do for now
     638        dlg = wx.MessageDialog(
     639            self, rdmsg,
     640            'Is this the file you want?',
     641            wx.YES_NO | wx.ICON_QUESTION,
     642            )
     643        dlg.SetSize((700,300)) # does not resize on Mac
     644        result = wx.ID_NO
     645        try:
     646            result = dlg.ShowModal()
     647        finally:
     648            dlg.Destroy()
     649        if result == wx.ID_NO: return True
     650        return False
     651   
     652    def OnImportGeneric(self,reader,readerlist,label,multiple=False,
     653        usedRanIdList=[],Preview=True,load2Tree=False):
     654        '''Used for all imports, including Phases, datasets, images...
     655
     656        Called from :meth:`GSASII.OnImportPhase`, :meth:`GSASII.OnImportImage`,
     657        :meth:`GSASII.OnImportSfact`, :meth:`GSASII.OnImportPowder`,
     658        :meth:`GSASII.OnImportSmallAngle` and :meth:'GSASII.OnImportReflectometry`
     659
     660        Uses reader_objects subclassed from :class:`GSASIIobj.ImportPhase`,
     661        :class:`GSASIIobj.ImportStructFactor`,
     662        :class:`GSASIIobj.ImportPowderData`,
     663        :class:`GSASIIobj.ImportSmallAngleData`
     664        :class:`GSASIIobj.ImportReflectometryData` or
     665        :class:`GSASIIobj.ImportImage`.
     666        If a specific reader is specified, only that method will be called,
     667        but if no reader is specified, every one that is potentially
     668        compatible (by file extension) will be tried on the file(s)
     669        selected in the Open File dialog.
     670
     671        :param reader_object reader: This will be a reference to
     672          a particular object to be used to read a file or None,
     673          if every appropriate reader should be used.
     674
     675        :param list readerlist: a list of reader objects appropriate for
     676          the current read attempt. At present, this will be either
     677          self.ImportPhaseReaderlist, self.ImportSfactReaderlist
     678          self.ImportPowderReaderlist or self.ImportImageReaderlist
     679          (defined in _init_Imports from the files found in the path),
     680          but in theory this list could be tailored.
     681          Used only when reader is None.
     682
     683        :param str label: string to place on the open file dialog:
     684          Open `label` input file
     685
     686        :param bool multiple: True if multiple files can be selected
     687          in the file dialog. False is default. At present True is used
     688          only for reading of powder data.
     689
     690        :param list usedRanIdList: an optional list of random Ids that
     691          have been used and should not be reused
     692
     693        :param bool Preview: indicates if a preview of the file should
     694          be shown. Default is True, but set to False for image files
     695          which are all binary.
     696
     697        :param bool load2Tree: indicates if the file should be loaded
     698          into the data tree immediately (used for images only). True
     699          only when called from :meth:`OnImportImage`; causes return
     700          value to change to a list of True values rather than
     701          reader objects.
     702
     703        :returns: a list of reader objects (rd_list) that were able
     704          to read the specified file(s). This list may be empty.
     705        '''
     706        self.lastimport = ''
     707        self.zipfile = None
     708        singlereader = True
     709        if reader is None:
     710            singlereader = False
     711            multiple = False
     712            #print "use all formats"
     713            choices = "any file (*.*)|*.*"
     714            choices += "|zip archive (.zip)|*.zip"
     715            extdict = {}
     716            # compile a list of allowed extensions
     717            for rd in readerlist:
     718                fmt = rd.formatName
     719                for extn in rd.extensionlist:
     720                    if not extdict.get(extn): extdict[extn] = []
     721                    extdict[extn] += [fmt,]
     722            for extn in sorted(extdict.keys(),cmp=lambda x,y: cmp(x.lower(), y.lower())):
     723                fmt = ''
     724                for f in extdict[extn]:
     725                    if fmt != "": fmt += ', '
     726                    fmt += f
     727                choices += "|" + fmt + " file (*" + extn + ")|*" + extn
     728        else:
     729            readerlist = [reader,]
     730            # compile a list of allowed extensions
     731            choices = reader.formatName + " file ("
     732            w = ""
     733            for extn in reader.extensionlist:
     734                if w != "": w += ";"
     735                w += "*" + extn
     736            choices += w + ")|" + w
     737            choices += "|zip archive (.zip)|*.zip"
     738            if not reader.strictExtension:
     739                choices += "|any file (*.*)|*.*"
     740        # get the file(s)
     741        if multiple:
     742            mode = wx.OPEN|wx.MULTIPLE
     743        else:
     744            mode = wx.OPEN
     745        filelist = G2G.GetImportFile(self,message="Choose "+label+" input file",
     746                    defaultFile="",wildcard=choices,style=mode)
     747        rd_list = []
     748        filelist1 = []
     749        for filename in filelist:
     750            # is this a zip file?
     751            if os.path.splitext(filename)[1].lower() == '.zip':
     752                extractedfiles = G2IO.ExtractFileFromZip(
     753                    filename,parent=self,
     754                    multipleselect=True)
     755                if extractedfiles is None: continue # error or Cancel
     756                if extractedfiles != filename:
     757                    self.zipfile = filename # save zip name
     758                    filelist1 += extractedfiles
     759                    continue
     760            filelist1.append(filename)
     761        filelist = filelist1
     762        Start = True    #1st time read - clear selections below
     763        for filename in filelist:
     764            # is this a zip file?
     765            if os.path.splitext(filename)[1].lower() == '.zip':
     766                extractedfile = G2IO.ExtractFileFromZip(filename,parent=self)
     767                if extractedfile is None: continue # error or Cancel
     768                if extractedfile != filename:
     769                    filename,self.zipfile = extractedfile,filename # now use the file that was created
     770            # determine which formats are compatible with this file
     771            primaryReaders = []
     772            secondaryReaders = []
     773            for rd in readerlist:
     774                flag = rd.ExtensionValidator(filename)
     775                if flag is None:
     776                    secondaryReaders.append(rd)
     777                elif flag:
     778                    primaryReaders.append(rd)
     779            if len(secondaryReaders) + len(primaryReaders) == 0 and reader:
     780                self.ErrorDialog('Not supported','The selected reader cannot read file '+filename)
     781                return []
     782            elif len(secondaryReaders) + len(primaryReaders) == 0:
     783                self.ErrorDialog('No Format','No matching format for file '+filename)
     784                return []
     785
     786            fp = None
     787            msg = ''
     788            fp = open(filename,'Ur')
     789            if len(filelist) == 1 and Preview:
     790                if self.PreviewFile(filename,fp): return []
     791            self.lastimport = filename # this is probably not what I want to do -- it saves only the
     792            # last name in a series. See rd.readfilename for a better name.
     793
     794            # try the file first with Readers that specify the
     795            # file's extension and later with ones that merely allow it
     796            errorReport = ''
     797            for rd in primaryReaders+secondaryReaders:
     798                if Start:   #clear old bank selections to allow new ones to be selected by user
     799                    rd.selections = []
     800                    rd.dnames = []
     801                rd.ReInitialize() # purge anything from a previous read
     802                fp.seek(0)  # rewind
     803                rd.errors = "" # clear out any old errors
     804                if not rd.ContentsValidator(fp): # rejected on cursory check
     805                    errorReport += "\n  "+rd.formatName + ' validator error'
     806                    if rd.errors:
     807                        errorReport += ': '+rd.errors
     808                    continue
     809                if len(rd.selections)>1 and Start:
     810                    dlg = G2G.G2MultiChoiceDialog(self,'Dataset Selector','Select data to read from the list below',rd.dnames)
     811                    if dlg.ShowModal() == wx.ID_OK:
     812                        rd.selections = dlg.GetSelections()
     813                    Start = False
     814                    dlg.Destroy()
     815                repeat = True
     816                rdbuffer = {} # create temporary storage for file reader
     817                block = 0
     818                fp.seek(0)  # rewind
     819                while repeat: # loop if the reader asks for another pass on the file
     820                    block += 1
     821                    repeat = False
     822                    rd.objname = os.path.basename(filename)
     823                    flag = False
     824                    if GSASIIpath.GetConfigValue('debug'): # allow exceptions for debugging
     825                        flag = rd.Reader(filename,fp,self,buffer=rdbuffer,blocknum=block,
     826                            usedRanIdList=usedRanIdList,)
     827                    else:
     828                        try:
     829                            flag = rd.Reader(filename,fp,self,buffer=rdbuffer,
     830                                blocknum=block,usedRanIdList=usedRanIdList,)
     831                        except rd.ImportException as detail:
     832                            rd.errors += "\n  Read exception: "+str(detail)
     833                        except Exception as detail:
     834                            import traceback
     835                            rd.errors += "\n  Unhandled read exception: "+str(detail)
     836                            rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
     837                    if flag: # this read succeeded
     838                        if rd.SciPy:        #was default read by scipy; needs 1 time fixes
     839                            G2IO.EditImageParms(self,rd.Data,rd.Comments,rd.Image,filename)
     840                            rd.SciPy = False
     841                        rd.readfilename = filename
     842                        if load2Tree:   #images only
     843                            if rd.repeatcount == 1 and not rd.repeat: # skip image number if only one in set
     844                                rd.Data['ImageTag'] = None
     845                            else:
     846                                rd.Data['ImageTag'] = rd.repeatcount
     847                            rd.Data['formatName'] = rd.formatName
     848                            if rd.sumfile:
     849                                rd.readfilename = rd.sumfile
     850                            G2IO.LoadImage2Tree(rd.readfilename,self,rd.Comments,rd.Data,rd.Npix,rd.Image)
     851                            rd_list.append(True) # save a stub the result before it is written over
     852                            del rd.Image
     853                        else:                                                   
     854                            rd_list.append(copy.deepcopy(rd)) # save the result before it is written over
     855                        if rd.repeat:
     856                            repeat = True
     857                        continue
     858                    errorReport += '\n'+rd.formatName + ' read error'
     859                    if rd.errors:
     860                        errorReport += ': '+rd.errors
     861                if rd_list: # read succeeded, was there a warning or any errors?
     862                    if rd.warnings:
     863                        self.ErrorDialog('Read Warning','The '+ rd.formatName+
     864                            ' reader reported a warning message:\n\n'+rd.warnings)
     865                    break # success in reading, try no further
     866            else:
     867                if singlereader:
     868                    print('The '+ rd.formatName+' reader was not able to read file '+filename+msg)
     869                    try:
     870                        print('\n\nError message(s):\n\t'+errorReport)
     871                    except:
     872                        pass
     873                    self.ErrorDialog('Read Error','The '+ rd.formatName+
     874                        ' reader was not able to read file '+filename+msg)
     875                else:
     876                    print('No reader was able to read file '+filename+msg)
     877                    try:
     878                        print('\n\nError message(s):\n\t'+errorReport)
     879                    except:
     880                        pass
     881                    self.ErrorDialog('Read Error','No reader was able to read file '+filename+msg)
     882            if fp: fp.close()
     883        return rd_list
     884
     885    def _Add_ImportMenu_Phase(self,parent):
     886        '''configure the Import Phase menus accord to the readers found in _init_Imports
     887        '''
     888        submenu = wx.Menu()
     889        item = parent.AppendMenu(wx.ID_ANY, 'Phase',
     890            submenu, help='Import phase data')
     891        for reader in self.ImportPhaseReaderlist:
     892            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
     893                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
     894            self.ImportMenuId[item.GetId()] = reader
     895            self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
     896        item = submenu.Append(wx.ID_ANY,help='Import phase data, use file to try to determine format',
     897            kind=wx.ITEM_NORMAL,text='guess format from file')
     898        self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
     899       
     900    def OnImportPhase(self,event):
     901        '''Called in response to an Import/Phase/... menu item
     902        to read phase information.
     903        dict self.ImportMenuId is used to look up the specific
     904        reader item associated with the menu item, which will be
     905        None for the last menu item, which is the "guess" option
     906        where all appropriate formats will be tried.
     907        '''
     908        # look up which format was requested
     909        reqrdr = self.ImportMenuId.get(event.GetId())
     910       
     911        # make a list of phase names, ranId's and the histograms used in those phases
     912        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
     913        phaseNameList = usedHistograms.keys() # phase names in use
     914        usedHKLFhists = [] # used single-crystal histograms
     915        for p in usedHistograms:
     916            for h in usedHistograms[p]:
     917                if h.startswith('HKLF ') and h not in usedHKLFhists:
     918                    usedHKLFhists.append(h)
     919                   
     920                   
     921        rdlist = self.OnImportGeneric(reqrdr,self.ImportPhaseReaderlist,
     922            'phase',usedRanIdList=phaseRIdList)
     923        if len(rdlist) == 0: return
     924        # for now rdlist is only expected to have one element
     925        # but below will allow multiple phases to be imported
     926        # if ever the import routines ever implement multiple phase reads.
     927        self.CheckNotebook()
     928        newPhaseList = []
     929        for rd in rdlist:
     930            PhaseName = ''
     931            dlg = wx.TextEntryDialog(self, 'Enter the name for the new phase',
     932                'Edit phase name', rd.Phase['General']['Name'],style=wx.OK)
     933            while PhaseName == '':
     934                dlg.CenterOnParent()
     935                if dlg.ShowModal() == wx.ID_OK:
     936                    PhaseName = dlg.GetValue().strip()
     937                else:
     938                    dlg.Destroy()
     939                    return
     940            dlg.Destroy()
     941            # make new phase names unique
     942            rd.Phase['General']['Name'] = G2obj.MakeUniqueLabel(PhaseName,phaseNameList)
     943            PhaseName = rd.Phase['General']['Name'][:]
     944            newPhaseList.append(PhaseName)
     945            print(u'Read phase {} from file {}'.format(PhaseName,self.lastimport))
     946            if not GetPatternTreeItemId(self,self.root,'Phases'):
     947                sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
     948            else:
     949                sub = GetPatternTreeItemId(self,self.root,'Phases')
     950            psub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
     951            self.PatternTree.SetItemPyData(psub,rd.Phase)
     952            self.PatternTree.Expand(self.root) # make sure phases are seen
     953            self.PatternTree.Expand(sub)
     954            self.PatternTree.Expand(psub)
     955            wx.CallAfter(SelectDataTreeItem,self,psub) #bring up new phase General tab
     956
     957            if rd.Constraints:
     958                sub = GetPatternTreeItemId(self,self.root,'Constraints') # was created in CheckNotebook if needed
     959                Constraints = self.PatternTree.GetItemPyData(sub)               
     960                # TODO: make sure that NEWVAR names are unique here?
     961                for i in rd.Constraints:
     962                    if type(i) is dict:
     963                        #for j in i: print j,' --> ',i[j]
     964                        if '_Explain' not in Constraints: Constraints['_Explain'] = {}
     965                        Constraints['_Explain'].update(i)
     966                        continue
     967                    Constraints['Phase'].append(i)
     968        if not newPhaseList: return # somehow, no new phases
     969        # get a list of existing histograms
     970        PWDRlist = []
     971        HKLFlist = []
     972        if self.PatternTree.GetCount():
     973            item, cookie = self.PatternTree.GetFirstChild(self.root)
     974            while item:
     975                name = self.PatternTree.GetItemText(item)
     976                if name.startswith('PWDR ') and name not in PWDRlist:
     977                    PWDRlist.append(name)
     978                if name.startswith('HKLF ') and name not in HKLFlist:
     979                    HKLFlist.append(name)
     980                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     981        TextList = PWDRlist + HKLFlist
     982        if not TextList:
     983            return          #no histograms
     984        header = 'Select histogram(s) to add to new phase(s):'
     985        for phaseName in newPhaseList:
     986            header += '\n  '+phaseName
     987
     988        notOK = True
     989        while notOK:
     990            result = G2G.ItemSelector(TextList,self,header,header='Add histogram(s)',multiple=True)
     991            if not result: return
     992            # check that selected single crystal histograms are not already in use!
     993            used = [TextList[i] for i in result if TextList[i] in usedHKLFhists]
     994            #for i in result:
     995            #    if TextList[i] in usedHKLFhists: used.append(TextList[i])
     996            if used:
     997                msg = 'The following single crystal histogram(s) are already in use'
     998                for i in used:
     999                    msg += '\n  '+str(i)
     1000                msg += '\nAre you sure you want to add them to this phase? '
     1001                msg += 'Associating a single crystal dataset to >1 histogram is usually an error, '
     1002                msg += 'so No is suggested here.'
     1003                if self.ErrorDialog('Likely error',msg,self,wtype=wx.YES_NO) == wx.ID_YES: notOK = False
     1004            else:
     1005                notOK = False
     1006        # connect new phases to histograms
     1007        sub = GetPatternTreeItemId(self,self.root,'Phases')
     1008        if not sub:
     1009            raise Exception('ERROR -- why are there no phases here?')
     1010        wx.BeginBusyCursor()
     1011        item, cookie = self.PatternTree.GetFirstChild(sub)
     1012        while item: # loop over (new) phases
     1013            phaseName = self.PatternTree.GetItemText(item)
     1014            data = self.PatternTree.GetItemPyData(item)
     1015            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     1016            if phaseName not in newPhaseList: continue
     1017            generalData = data['General']
     1018            SGData = generalData['SGData']
     1019            Super = generalData.get('Super',0)
     1020            SuperVec = []
     1021            if Super:
     1022                SuperVec = np.array(generalData['SuperVec'][0])
     1023            UseList = data['Histograms']
     1024            NShkl = len(G2spc.MustrainNames(SGData))
     1025            NDij = len(G2spc.HStrainNames(SGData))
     1026            for i in result:
     1027                histoName = TextList[i]
     1028                if histoName in HKLFlist:
     1029                    #redo UpdateHKLFdata(histoName) here:
     1030                    Id = GetPatternTreeItemId(self,self.root,histoName)
     1031                    refDict,reflData = self.PatternTree.GetItemPyData(Id)
     1032                    G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
     1033                    Super = reflData.get('Super',0)
     1034                    for iref,ref in enumerate(reflData['RefList']):
     1035                        hkl = ref[:3]
     1036                        if Super:
     1037                            H = list(hkl+SuperVec*ref[3])
     1038                        else:
     1039                            H = hkl
     1040                        ref[4+Super] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
     1041                        iabsnt = G2spc.GenHKLf(H,SGData)[0]
     1042                        if iabsnt:  #flag space gp. absences
     1043                            if Super:
     1044                                if not ref[2+Super]:
     1045                                    ref[3+Super] = 0
     1046                                else:
     1047                                    ref[3+Super] = 1    #twin id
     1048                            else:
     1049                                ref[3] = 0
     1050                    UseList[histoName] = SetDefaultDData(reflData['Type'],histoName)
     1051                elif histoName in PWDRlist:
     1052                    Id = GetPatternTreeItemId(self,self.root,histoName)
     1053                    refList = self.PatternTree.GetItemPyData(
     1054                        GetPatternTreeItemId(self,Id,'Reflection Lists'))
     1055                    refList[generalData['Name']] = {}
     1056                    UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
     1057                else:
     1058                    raise Exception('Unexpected histogram '+histoName)
     1059        wx.EndBusyCursor()
     1060        self.EnableRefineCommand()
     1061       
     1062        return # success
     1063       
     1064    def _Add_ImportMenu_Image(self,parent):
     1065        '''configure the Import Image menus accord to the readers found in _init_Imports
     1066        '''
     1067        submenu = wx.Menu()
     1068        item = parent.AppendMenu(wx.ID_ANY, 'Image',
     1069            submenu, help='Import image file')
     1070        for reader in self.ImportImageReaderlist:
     1071            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
     1072                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
     1073            self.ImportMenuId[item.GetId()] = reader
     1074            self.Bind(wx.EVT_MENU, self.OnImportImage, id=item.GetId())
     1075        item = submenu.Append(wx.ID_ANY,help='Import image data, use file to try to determine format',
     1076            kind=wx.ITEM_NORMAL,text='guess format from file')
     1077        self.Bind(wx.EVT_MENU, self.OnImportImage, id=item.GetId())
     1078       
     1079    def OnImportImage(self,event):
     1080        '''Called in response to an Import/Image/... menu item
     1081        to read an image from a file. Like all the other imports,
     1082        dict self.ImportMenuId is used to look up the specific
     1083        reader item associated with the menu item, which will be
     1084        None for the last menu item, which is the "guess" option
     1085        where all appropriate formats will be tried.
     1086
     1087        A reader object is filled each time an image is read.
     1088        '''
     1089        self.CheckNotebook()
     1090        # look up which format was requested
     1091        reqrdr = self.ImportMenuId.get(event.GetId())
     1092        rdlist = self.OnImportGeneric(reqrdr,self.ImportImageReaderlist,
     1093            'image',multiple=True,Preview=False,load2Tree=True)
     1094        if rdlist:
     1095            self.PatternTree.SelectItem(GetPatternTreeItemId(self,self.Image,'Image Controls'))             #show last image to have beeen read
     1096                   
     1097    def _Add_ImportMenu_Sfact(self,parent):
     1098        '''configure the Import Structure Factor menus accord to the readers found in _init_Imports
     1099        '''
     1100        submenu = wx.Menu()
     1101        item = parent.AppendMenu(wx.ID_ANY, 'Structure Factor',
     1102            submenu, help='Import Structure Factor data')
     1103        for reader in self.ImportSfactReaderlist:
     1104            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,               
     1105                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
     1106            self.ImportMenuId[item.GetId()] = reader
     1107            self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
     1108        item = submenu.Append(wx.ID_ANY,
     1109            help='Import Structure Factor, use file to try to determine format',
     1110            kind=wx.ITEM_NORMAL,
     1111            text='guess format from file')
     1112        self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
     1113
     1114    def OnImportSfact(self,event):
     1115        '''Called in response to an Import/Structure Factor/... menu item
     1116        to read single crystal datasets.
     1117        dict self.ImportMenuId is used to look up the specific
     1118        reader item associated with the menu item, which will be
     1119        None for the last menu item, which is the "guess" option
     1120        where all appropriate formats will be tried.
     1121        '''
     1122        # get a list of existing histograms
     1123        HKLFlist = []
     1124        if self.PatternTree.GetCount():
     1125            item, cookie = self.PatternTree.GetFirstChild(self.root)
     1126            while item:
     1127                name = self.PatternTree.GetItemText(item)
     1128                if name.startswith('HKLF ') and name not in HKLFlist:
     1129                    HKLFlist.append(name)
     1130                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     1131        # look up which format was requested
     1132        reqrdr = self.ImportMenuId.get(event.GetId())
     1133        rdlist = self.OnImportGeneric(reqrdr,self.ImportSfactReaderlist,
     1134            'Structure Factor',multiple=True)
     1135        if len(rdlist) == 0: return
     1136        self.CheckNotebook()
     1137        newHistList = []
     1138        for rd in rdlist:
     1139            HistName = rd.objname
     1140            if len(rdlist) <= 2:
     1141                dlg = wx.TextEntryDialog( # allow editing of Structure Factor name
     1142                    self, 'Enter the name for the new Structure Factor',
     1143                    'Edit Structure Factor name', HistName,
     1144                    style=wx.OK)
     1145                dlg.CenterOnParent()
     1146                if dlg.ShowModal() == wx.ID_OK:
     1147                    HistName = dlg.GetValue()
     1148                dlg.Destroy()
     1149            HistName = 'HKLF '+G2obj.StripUnicode(HistName,'_')
     1150            # make new histogram names unique
     1151            if len(rd.Banks):
     1152                for Bank in rd.Banks:
     1153                    valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
     1154                    HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
     1155                    print 'Read structure factor table '+HistName+' from file '+self.lastimport
     1156                    Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
     1157                    if not Bank['RefDict'].get('FF'):
     1158                        Bank['RefDict']['FF'] = {}
     1159                    self.PatternTree.SetItemPyData(Id,[valuesdict,Bank['RefDict']])
     1160                    Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
     1161                    self.PatternTree.SetItemPyData(Sub,copy.copy(rd.Parameters))
     1162                    self.PatternTree.SetItemPyData(
     1163                        self.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
     1164                    newHistList.append(HistName)
     1165            else:
     1166                valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
     1167                HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
     1168                print 'Read structure factor table '+HistName+' from file '+self.lastimport
     1169                if not rd.RefDict.get('FF'):
     1170                    rd.RefDict['FF'] = {}
     1171                Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
     1172                self.PatternTree.SetItemPyData(Id,[valuesdict,rd.RefDict])
     1173                Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
     1174                self.PatternTree.SetItemPyData(Sub,rd.Parameters)
     1175                self.PatternTree.SetItemPyData(
     1176                    self.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
     1177                newHistList.append(HistName)
     1178               
     1179            self.PatternTree.SelectItem(Id)
     1180            self.PatternTree.Expand(Id)
     1181            self.Sngl = True
     1182
     1183        if not newHistList: return # somehow, no new histograms
     1184        # make a list of phase names
     1185        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
     1186        phaseNameList = usedHistograms.keys() # phase names in use
     1187        if not phaseNameList: return # no phases yet, nothing to do
     1188        header = 'Select phase(s) to add the new\nsingle crystal dataset(s) to:'
     1189        for Name in newHistList:
     1190            header += '\n  '+str(Name)
     1191        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
     1192        if not result: return
     1193        # connect new phases to histograms
     1194        sub = GetPatternTreeItemId(self,self.root,'Phases')
     1195        if not sub:
     1196            raise Exception('ERROR -- why are there no phases here?')
     1197        wx.BeginBusyCursor()
     1198        item, cookie = self.PatternTree.GetFirstChild(sub)
     1199        iph = -1
     1200        while item: # loop over (new) phases
     1201            iph += 1
     1202            data = self.PatternTree.GetItemPyData(item)
     1203            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     1204            if iph not in result: continue
     1205            generalData = data['General']
     1206            SGData = generalData['SGData']
     1207            Super = generalData.get('Super',0)
     1208            SuperVec = []
     1209            if Super:
     1210                SuperVec = np.array(generalData['SuperVec'][0])
     1211            UseList = data['Histograms']
     1212            for histoName in newHistList:
     1213                #redo UpdateHKLFdata(histoName) here:
     1214                Id = GetPatternTreeItemId(self,self.root,histoName)
     1215                refDict,reflData = self.PatternTree.GetItemPyData(Id)
     1216                UseList[histoName] = SetDefaultDData(reflData['Type'],histoName)
     1217                G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
     1218                if 'TwMax' in reflData:     #nonmerohedral twins present
     1219                    UseList[histoName]['Twins'] = []
     1220                    for iT in range(reflData['TwMax'][0]+1):
     1221                        if iT in reflData['TwMax'][1]:
     1222                            UseList[histoName]['Twins'].append([False,0.0])
     1223                        else:
     1224                            UseList[histoName]['Twins'].append([np.array([[1,0,0],[0,1,0],[0,0,1]]),[1.0,False,reflData['TwMax'][0]]])
     1225                else:   #no nonmerohedral twins
     1226                    UseList[histoName]['Twins'] = [[np.array([[1,0,0],[0,1,0],[0,0,1]]),[1.0,False,0]],]
     1227                for iref,ref in enumerate(reflData['RefList']):
     1228                    hkl = ref[:3]
     1229                    if Super:
     1230                        H = list(hkl+SuperVec*ref[3])
     1231                    else:
     1232                        H = hkl
     1233                    ref[4+Super] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
     1234                    iabsnt,mul,Uniq,phi = G2spc.GenHKLf(H,SGData)
     1235                    if iabsnt:  #flag space gp. absences
     1236                        if Super:
     1237                            if not ref[2+Super]:
     1238                                ref[3+Super] = 0
     1239                            else:
     1240                                ref[3+Super] = 1    #twin id?
     1241                        else:
     1242                            ref[3] = 0
     1243        wx.EndBusyCursor()
     1244        self.EnableRefineCommand()       
     1245        return # success
     1246
     1247    def _Add_ImportMenu_powder(self,parent):
     1248        '''configure the Powder Data menus accord to the readers found in _init_Imports
     1249        '''
     1250        submenu = wx.Menu()
     1251        item = parent.AppendMenu(wx.ID_ANY, 'Powder Data',
     1252            submenu, help='Import Powder data')
     1253        for reader in self.ImportPowderReaderlist:
     1254            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
     1255                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
     1256            self.ImportMenuId[item.GetId()] = reader
     1257            self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
     1258        item = submenu.Append(wx.ID_ANY,
     1259            help='Import powder data, use file to try to determine format',
     1260            kind=wx.ITEM_NORMAL,text='guess format from file')
     1261        self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
     1262        submenu.AppendSeparator()
     1263        item = submenu.Append(wx.ID_ANY,
     1264            help='Create a powder data set entry that will be simulated',
     1265            kind=wx.ITEM_NORMAL,text='Simulate a dataset')
     1266        self.Bind(wx.EVT_MENU, self.OnDummyPowder, id=item.GetId())
     1267       
     1268    def OpenPowderInstprm(self,instfile):
     1269        '''Read a GSAS-II (new) instrument parameter file
     1270
     1271        :param str instfile: name of instrument parameter file
     1272
     1273        '''
     1274        File = open(instfile,'r')
     1275        lines = File.readlines()
     1276        File.close()
     1277        return lines       
     1278           
     1279    def ReadPowderInstprm(self,instLines,bank,databanks,rd):
     1280        '''Read lines from a GSAS-II (new) instrument parameter file
     1281        similar to G2pwdGUI.OnLoad
     1282        If instprm file has multiple banks each with header #Bank n: ..., this
     1283        finds matching bank no. to load - problem with nonmatches?
     1284
     1285        :param list instLines: strings from GSAS-II parameter file; can be concatenated with ';'
     1286        :param int  bank: bank number to check when instprm file has '#BANK n:...' strings
     1287            when bank = n then use parameters; otherwise skip that set. Ignored if BANK n:
     1288            not present. NB: this kind of instprm file made by a Save all profile command in Instrument Parameters
     1289        :return dict: Inst  instrument parameter dict if OK, or
     1290                str: Error message if failed   
     1291        '''
     1292        if 'GSAS-II' not in instLines[0]: # not a valid file
     1293            return 'Not a valid GSAS-II instprm file'
     1294        newItems = []
     1295        newVals = []
     1296        Found = False
     1297        il = 0
     1298        if bank is None: # no bank was specified in the input file, is more than one present in file?
     1299            banklist = set([])
     1300            for S in instLines:
     1301                if S[0] == '#' and 'Bank' in S:
     1302                    banklist.add(int(S.split(':')[0].split()[1]))
     1303            if len(banklist) > 1: # yes, the user must make a selection
     1304                choices = [str(i) for i in banklist]
     1305                bank = int(G2G.ItemSelector(choices,self.G2frame,multiple=False))
     1306            else:
     1307                bank = 1
     1308            rd.powderentry[2] = bank
     1309        while il < len(instLines):
     1310            S = instLines[il]
     1311            if S[0] == '#':
     1312                if Found:
     1313                    break
     1314                if 'Bank' in S:
     1315                    if bank == int(S.split(':')[0].split()[1]):
     1316                        il += 1
     1317                        S = instLines[il]
     1318                    else:
     1319                        il += 1
     1320                        S = instLines[il]
     1321                        while il < len(instLines) and '#Bank' not in S:
     1322                            il += 1
     1323                            if il == len(instLines):
     1324                                return 'Bank %d not found in instprm file'%(bank)
     1325                            S = instLines[il]
     1326                        continue
     1327                else:   #a non #Bank file
     1328                    il += 1
     1329                    S = instLines[il]
     1330            Found = True
     1331            if '"""' in S:
     1332                delim = '"""'
     1333            elif "'''" in S:
     1334                delim = "'''"
     1335            else:
     1336                S = S.replace(' ','')
     1337                SS = S.strip().split(';')
     1338                for s in SS:
     1339                    [item,val] = s.split(':',1)
     1340                    newItems.append(item)
     1341                    try:
     1342                        newVals.append(float(val))
     1343                    except ValueError:
     1344                        newVals.append(val)
     1345                il += 1
     1346                continue
     1347            # read multiline values, delimited by ''' or """
     1348            item,val = S.strip().split(':',1)
     1349            val = val.replace(delim,'').rstrip()
     1350            val += '\n'
     1351            while True:
     1352                il += 1
     1353                if il >= len(instLines): break
     1354                S = instLines[il]
     1355                if delim in S:
     1356                    val += S.replace(delim,'').rstrip()
     1357                    val += '\n'
     1358                    break
     1359                else:
     1360                    val += S.rstrip()
     1361                    val += '\n'
     1362            newItems.append(item)
     1363            newVals.append(val)
     1364            il += 1
     1365        return [G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}]
     1366       
     1367    def ReadPowderIparm(self,instfile,bank,databanks,rd):
     1368        '''Read a GSAS (old) instrument parameter file
     1369
     1370        :param str instfile: name of instrument parameter file
     1371        :param int bank: the bank number read in the raw data file
     1372        :param int databanks: the number of banks in the raw data file.
     1373          If the number of banks in the data and instrument parameter files
     1374          agree, then the sets of banks are assumed to match up and bank
     1375          is used to select the instrument parameter file. If not and not TOF,
     1376          the user is asked to make a selection.
     1377        :param obj rd: the raw data (histogram) data object. This
     1378          sets rd.instbank.
     1379
     1380        '''
     1381        if not os.path.exists(instfile): # no such file
     1382            return {}
     1383        fp = 0
     1384        try:
     1385            fp = open(instfile,'Ur')
     1386            Iparm = {}
     1387            for S in fp:
     1388                if '#' in S[0]:
     1389                    continue
     1390                Iparm[S[:12]] = S[12:-1]
     1391        except IOError:
     1392            print(u'Error reading file: {}'.format(instfile))
     1393        if fp:       
     1394            fp.close()
     1395
     1396        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
     1397        if ibanks == 1: # there is only one bank here, return it
     1398            rd.instbank = 1
     1399            rd.powderentry[2] = 1
     1400            return Iparm
     1401        if 'PNT' in Iparm['INS   HTYPE ']:      #allow mismatch between banks in data  iparm file for TOF
     1402            rd.instbank = bank
     1403        elif ibanks != databanks or bank is None:
     1404            choices = []
     1405            for i in range(1,1+ibanks):
     1406                choices.append('Bank '+str(i))
     1407            bank = 1 + G2IO.BlockSelector(
     1408                choices, self,
     1409                title='Select an instrument parameter bank for '+
     1410                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
     1411                '\nOr use Cancel to select from the default parameter sets',
     1412                header='Block Selector')
     1413        if bank is None: return {}
     1414        # pull out requested bank # bank from the data, and change the bank to 1
     1415        IparmS = {}
     1416        for key in Iparm:
     1417            if 'INS' in key[:3]:    #skip around rubbish lines in some old iparm files
     1418                if key[4:6] == "  ":
     1419                    IparmS[key] = Iparm[key]
     1420                elif int(key[4:6].strip()) == bank:
     1421                    IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
     1422        rd.instbank = bank
     1423        return IparmS
     1424                       
     1425    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
     1426        '''Open and read an instrument parameter file for a data file
     1427        Returns the list of parameters used in the data tree
     1428
     1429        :param obj rd: the raw data (histogram) data object.
     1430
     1431        :param str prevIparm: not used
     1432
     1433        :param str lastIparmfile: Name of last instrument parameter
     1434          file that was read, or a empty string.
     1435
     1436        :param str lastdatafile: Name of last data file that was read.
     1437
     1438        :returns: a list of two dicts, the first containing instrument parameters
     1439          and the second used for TOF lookup tables for profile coeff.
     1440
     1441        '''
     1442        def SetPowderInstParms(Iparm, rd):
     1443            '''extracts values from instrument parameters in rd.instdict
     1444            or in array Iparm.
     1445            Create and return the contents of the instrument parameter tree entry.
     1446            '''
     1447            Irads = {0:' ',1:'CrKa',2:'FeKa',3:'CuKa',4:'MoKa',5:'AgKa',6:'TiKa',7:'CoKa'}
     1448            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
     1449            # override inst values with values read from data file
     1450            Bank = rd.powderentry[2]    #should be used in multibank iparm files
     1451            if rd.instdict.get('type'):
     1452                DataType = rd.instdict.get('type')
     1453            data = [DataType,]
     1454            instname = Iparm.get('INS  1INAME ')
     1455            irad = int(Iparm.get('INS  1 IRAD ','0'))
     1456            if instname:
     1457                rd.Sample['InstrName'] = instname.strip()
     1458            if 'C' in DataType:
     1459                wave1 = None
     1460                wave2 = 0.0
     1461                if rd.instdict.get('wave'):
     1462                    wl = rd.instdict.get('wave')
     1463                    wave1 = wl[0]
     1464                    if len(wl) > 1: wave2 = wl[1]
     1465                s = Iparm['INS  1 ICONS']
     1466                if not wave1:
     1467                    wave1 = G2IO.sfloat(s[:10])
     1468                    wave2 = G2IO.sfloat(s[10:20])
     1469                v = (wave1,wave2,
     1470                     G2IO.sfloat(s[20:30])/100.,G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
     1471                if not v[1]:
     1472                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
     1473                    v = (v[0],v[2],v[4])
     1474                    codes = [0,0,0,0]
     1475                    rd.Sample.update({'Type':'Debye-Scherrer','Absorption':[0.,False],'DisplaceX':[0.,False],'DisplaceY':[0.,False]})
     1476                else:
     1477                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
     1478                    codes = [0,0,0,0,0,0]
     1479                    rd.Sample.update({'Type':'Bragg-Brentano','Shift':[0.,False],'Transparency':[0.,False],
     1480                        'SurfRoughA':[0.,False],'SurfRoughB':[0.,False]})
     1481                data.extend(v)
     1482                if 'INS  1PRCF  ' in Iparm:
     1483                    v1 = Iparm['INS  1PRCF  '].split()                                                 
     1484                    v = Iparm['INS  1PRCF 1'].split()
     1485                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
     1486                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
     1487                    v = Iparm['INS  1PRCF 2'].split()
     1488                    if v1[0] == 3:
     1489                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
     1490                    else:
     1491                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file                   
     1492                else:
     1493                    v1 = Iparm['INS  1PRCF1 '].split()                                                 
     1494                    v = Iparm['INS  1PRCF11'].split()
     1495                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
     1496                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
     1497                    v = Iparm['INS  1PRCF12'].split()
     1498                    if v1[0] == 3:
     1499                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
     1500                    else:
     1501                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
     1502                codes.extend([0,0,0,0,0,0,0])
     1503                Iparm1 = G2IO.makeInstDict(names,data,codes)
     1504                Iparm1['Source'] = [Irads[irad],Irads[irad]]
     1505                Iparm1['Bank'] = [Bank,Bank,0]
     1506                return [Iparm1,{}]
     1507            elif 'T' in DataType:
     1508                names = ['Type','fltPath','2-theta','difC','difA', 'difB','Zero','alpha','beta-0','beta-1',
     1509                    'beta-q','sig-0','sig-1','sig-2','sig-q', 'X','Y','Azimuth',]
     1510                codes = [0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,]
     1511                azm = 0.
     1512                if 'INS  1DETAZM' in Iparm:
     1513                    azm = float(Iparm['INS  1DETAZM'])
     1514                rd.Sample['Azimuth'] = azm
     1515                fltPath0 = 20.                      #arbitrary
     1516                if 'INS   FPATH1' in Iparm:                   
     1517                    s = Iparm['INS   FPATH1'].split()
     1518                    fltPath0 = G2IO.sfloat(s[0])
     1519                if 'INS  1BNKPAR' not in Iparm:     #bank missing from Iparm file
     1520                    return []
     1521                s = Iparm['INS  1BNKPAR'].split()
     1522                fltPath1 = G2IO.sfloat(s[0])
     1523                data.extend([fltPath0+fltPath1,])               #Flight path source-sample-detector
     1524                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
     1525                s = Iparm['INS  1 ICONS'].split()
     1526                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,G2IO.sfloat(s[2])])    #difC,difA,difB,Zero
     1527                if 'INS  1PRCF  ' in Iparm:
     1528                    s = Iparm['INS  1PRCF  '].split()
     1529                    pfType = int(s[0])
     1530                    s = Iparm['INS  1PRCF 1'].split()
     1531                    if abs(pfType) == 1:
     1532                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
     1533                        s = Iparm['INS  1PRCF 2'].split()
     1534                        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
     1535                    elif abs(pfType) in [3,4,5]:
     1536                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
     1537                        if abs(pfType) == 4:
     1538                            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
     1539                        else:
     1540                            s = Iparm['INS  1PRCF 2'].split()
     1541                            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                       
     1542                    elif abs(pfType) == 2:
     1543                        data.extend([G2IO.sfloat(s[1]),0.0,1./G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
     1544                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                           
     1545                else:
     1546                    s = Iparm['INS  1PRCF1 '].split()
     1547                    pfType = int(s[0])
     1548                    s = Iparm['INS  1PRCF11'].split()
     1549                    if abs(pfType) == 1:
     1550                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
     1551                        s = Iparm['INS  1PRCF12'].split()
     1552                        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
     1553                    elif abs(pfType) in [3,4,5]:
     1554                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
     1555                        if abs(pfType) == 4:
     1556                            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
     1557                        else:
     1558                            s = Iparm['INS  1PRCF12'].split()
     1559                            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                       
     1560                Inst1 = G2IO.makeInstDict(names,data,codes)
     1561                Inst1['Bank'] = [Bank,Bank,0]
     1562                Inst2 = {}
     1563                if pfType < 0:
     1564                    Ipab = 'INS  1PAB'+str(-pfType)
     1565                    Npab = int(Iparm[Ipab+'  '].strip())
     1566                    Inst2['Pdabc'] = []
     1567                    for i in range(Npab):
     1568                        k = Ipab+str(i+1).rjust(2)
     1569                        s = Iparm[k].split()
     1570                        Inst2['Pdabc'].append([float(t) for t in s])
     1571                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
     1572                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
     1573                if 'INS  1I ITYP' in Iparm:
     1574                    s = Iparm['INS  1I ITYP'].split()
     1575                    Ityp = int(s[0])
     1576                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
     1577                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
     1578                    if Ityp in [1,2,4]:
     1579                        Inst2['Itype'] = Itypes[Ityp-1]
     1580                        Inst2['Tminmax'] = Tminmax
     1581                        Icoeff = []
     1582                        Iesd = []
     1583                        Icovar = []                   
     1584                        for i in range(3):
     1585                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
     1586                            Icoeff += [float(S) for S in s]
     1587                            s = Iparm['INS  1IECOF'+str(i+1)].split()
     1588                            Iesd += [float(S) for S in s]
     1589                        NT = 10
     1590                        for i in range(8):
     1591                            s = Iparm['INS  1IECOR'+str(i+1)]
     1592                            if i == 7:
     1593                                NT = 8
     1594                            Icovar += [float(s[6*j:6*j+6]) for j in range(NT)]
     1595                        Inst2['Icoeff'] = Icoeff
     1596                        Inst2['Iesd'] = Iesd
     1597                        Inst2['Icovar'] = Icovar
     1598                return [Inst1,Inst2]
     1599               
     1600        def GetDefaultParms(self,rd):
     1601            '''Solicits from user a default set of parameters & returns Inst parm dict
     1602            param: self: refers to the GSASII main class
     1603            param: rd: importer data structure
     1604            returns: dict: Instrument parameter dictionary
     1605            '''       
     1606            sind = lambda x: math.sin(x*math.pi/180.)
     1607            tand = lambda x: math.tan(x*math.pi/180.)
     1608            import defaultIparms as dI
     1609            while True: # loop until we get a choice
     1610                choices = []
     1611                head = 'Select from default instrument parameters for '+rd.idstring
     1612   
     1613                for l in dI.defaultIparm_lbl:
     1614                    choices.append('Defaults for '+l)
     1615                res = G2IO.BlockSelector(choices,ParentFrame=self,title=head,
     1616                    header='Select default inst parms',useCancel=True)
     1617                if res is None: return None
     1618                rd.instfile = ''
     1619                if 'lab data' in choices[res]:
     1620                    rd.Sample.update({'Type':'Bragg-Brentano','Shift':[0.,False],'Transparency':[0.,False],
     1621                        'SurfRoughA':[0.,False],'SurfRoughB':[0.,False]})
     1622                else:
     1623                    rd.Sample.update({'Type':'Debye-Scherrer','Absorption':[0.,False],'DisplaceX':[0.,False],
     1624                        'DisplaceY':[0.,False]})
     1625                if 'Generic' in choices[res]:
     1626                    dlg = G2G.MultiFloatDialog(self,title='Generic TOF detector bank',
     1627                        prompts=['Total FP','2-theta',],values=[25.0,150.,],
     1628                            limits=[[6.,200.],[5.,175.],],formats=['%6.2f','%6.1f',])
     1629                    if dlg.ShowModal() == wx.ID_OK: #strictly empirical approx.
     1630                        FP,tth = dlg.GetValues()
     1631                        difC = 505.632*FP*sind(tth/2.)
     1632                        sig1 = 50.+2.5e-6*(difC/tand(tth/2.))**2
     1633                        bet1 = .00226+7.76e+11/difC**4
     1634                        rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
     1635                        Inst = self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)
     1636                        Inst[0]['difC'] = [difC,difC,0]
     1637                        Inst[0]['sig-1'] = [sig1,sig1,0]
     1638                        Inst[0]['beta-1'] = [bet1,bet1,0]
     1639                        return Inst    #this is [Inst1,Inst2] a pair of dicts
     1640                    dlg.Destroy()
     1641                else:
     1642                    rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
     1643                    return self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
     1644
     1645        # stuff we might need from the reader
     1646        filename = rd.powderentry[0]
     1647        bank = rd.powderentry[2]
     1648        numbanks = rd.numbanks
     1649        #1st priority: is there an instrument parameter file matching the current file
     1650        # with extension .instprm, .prm, .inst, or .ins? If so read it
     1651        basename = os.path.splitext(filename)[0]
     1652        for ext in '.prm','.inst','.ins','.instprm':
     1653            if self.zipfile:
     1654                instfile = G2IO.ExtractFileFromZip(self.zipfile,
     1655                    selection=os.path.split(basename + ext)[1],parent=self)
     1656                if instfile == None:
     1657                    continue
     1658            else:
     1659                instfile = basename + ext
     1660            if not os.path.exists(instfile):
     1661                continue
     1662            if 'instprm' in instfile:
     1663                Lines = self.OpenPowderInstprm(instfile)
     1664                instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
     1665                if 'list' in str(type(instParmList)):
     1666                    rd.instfile = instfile
     1667                    rd.instmsg = 'GSAS-II file '+instfile
     1668                    return instParmList
     1669                else:
     1670                    #print 'debug: open/read failed',instfile
     1671                    pass # fail silently
     1672            else:
     1673                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
     1674                if Iparm:
     1675                    #print 'debug: success'
     1676                    rd.instfile = instfile
     1677                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
     1678                    return SetPowderInstParms(Iparm,rd)
     1679                else:
     1680                    #print 'debug: open/read failed',instfile
     1681                    pass # fail silently
     1682
     1683        #2nd priority: is there an instrument parameter file defined for the current data set?
     1684        # or if this is a read on a set of set of files, use the last one again
     1685        #if rd.instparm as found in data file header or (lastdatafile == filename and lastIparmfile):
     1686        if rd.instparm or lastIparmfile:
     1687            if rd.instparm:
     1688                instfile = os.path.join(os.path.split(filename)[0],rd.instparm)
     1689            else:
     1690                # for multiple reads of one data file, reuse the inst parm file
     1691                instfile = lastIparmfile
     1692#            if self.zipfile:
     1693#                instfile = G2IO.ExtractFileFromZip(self.zipfile,
     1694#                    selection=os.path.split(instfile)[1],parent=self)
     1695            if instfile != None and os.path.exists(instfile):
     1696                #print 'debug: try read',instfile
     1697                if 'instprm' in instfile:   #GSAS-II file must have .instprm as extension
     1698                    Lines = self.OpenPowderInstprm(instfile)
     1699                    if Lines is not None:
     1700                        instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)   #this is [Inst1,Inst2] a pair of dicts
     1701                else:   #old GSAS style iparm file - could be named anything!
     1702                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
     1703                    if Iparm:
     1704                        #print 'debug: success'
     1705                        rd.instfile = instfile
     1706                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
     1707                        instParmList = SetPowderInstParms(Iparm,rd)     #this is [Inst1,Inst2] a pair of dicts
     1708                if 'list' in str(type(instParmList)):   #record stuff & return stuff
     1709                    rd.instfile = instfile
     1710                    rd.instmsg = 'GSAS-II file '+instfile
     1711                    return instParmList
     1712                else:   #bad iparms - try default
     1713                    rd.instmsg = instParmList   #an error message
     1714                    return GetDefaultParms(self,rd)
     1715            else:
     1716                self.ErrorDialog('Open Error',u'Error opening instrument parameter file '   \
     1717                    +'{} requested by file '.format(instfile,filename))
     1718        #Finally - ask user for Instrument parametrs file - seems it can't be in a zip file
     1719        while True: # loop until we get a file that works or we get a cancel
     1720            instfile = ''
     1721            pth = G2G.GetImportPath(self)
     1722            if not pth: pth = '.'
     1723            dlg = wx.FileDialog(self,
     1724                'Choose inst. param file for "'+rd.idstring+'" (or Cancel for default)',
     1725                pth, '',
     1726                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
     1727                'GSAS-II iparm file (*.instprm)|*.instprm|'
     1728                'All files (*.*)|*.*', wx.OPEN)
     1729            if os.path.exists(lastIparmfile):
     1730                dlg.SetFilename(lastIparmfile)
     1731            if dlg.ShowModal() == wx.ID_OK:
     1732                instfile = dlg.GetPath()
     1733            dlg.Destroy()
     1734            if not instfile:
     1735                return GetDefaultParms(self,rd) #on Cancel/break
     1736            if 'instprm' in instfile:
     1737                Lines = self.OpenPowderInstprm(instfile)
     1738                if Lines is not None:
     1739                    instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
     1740                if 'list' in str(type(instParmList)):
     1741                    rd.instfile = instfile
     1742                    rd.instmsg = 'GSAS-II file '+instfile
     1743                    return instParmList
     1744                else:
     1745                    rd.instmsg = instParmList   #an error message
     1746                    return GetDefaultParms(self,rd)
     1747            else:
     1748                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
     1749                if Iparm:
     1750                    #print 'debug: success with',instfile
     1751                    rd.instfile = instfile
     1752                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
     1753                    return SetPowderInstParms(Iparm,rd)
     1754                else:
     1755                    self.ErrorDialog('Read Error',
     1756                                     u'Error opening/reading file {}'.format(instfile))
     1757    def EnableRefineCommand(self):
     1758        haveData = False
     1759        # check for phases connected to histograms
     1760        sub = GetPatternTreeItemId(self,self.root,'Phases')
     1761        if not sub: return
     1762        item, cookie = self.PatternTree.GetFirstChild(sub)
     1763        while item: # loop over phases
     1764            data = self.PatternTree.GetItemPyData(item)
     1765            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     1766            UseList = data['Histograms']
     1767            if UseList: haveData = True
     1768        if haveData:
     1769            self.dataWindow.DataMenu.Enable(wxID_DATADELETE,True)
     1770            for item in self.Refine: item.Enable(True)
     1771        else:
     1772            self.dataWindow.DataMenu.Enable(wxID_DATADELETE,False)
     1773            for item in self.Refine: item.Enable(False)
     1774
     1775       
     1776    def OnImportPowder(self,event):
     1777        '''Called in response to an Import/Powder Data/... menu item
     1778        to read a powder diffraction data set.
     1779        dict self.ImportMenuId is used to look up the specific
     1780        reader item associated with the menu item, which will be
     1781        None for the last menu item, which is the "guess" option
     1782        where all appropriate formats will be tried.
     1783
     1784        Also reads an instrument parameter file for each dataset.
     1785        '''
     1786        # get a list of existing histograms
     1787        PWDRlist = []
     1788        if self.PatternTree.GetCount():
     1789            item, cookie = self.PatternTree.GetFirstChild(self.root)
     1790            while item:
     1791                name = self.PatternTree.GetItemText(item)
     1792                if name.startswith('PWDR ') and name not in PWDRlist:
     1793                    PWDRlist.append(name)
     1794                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     1795        # look up which format was requested
     1796        reqrdr = self.ImportMenuId.get(event.GetId()) 
     1797        rdlist = self.OnImportGeneric(
     1798            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
     1799        if len(rdlist) == 0: return
     1800        self.CheckNotebook()
     1801        Iparm = None
     1802        lastIparmfile = ''
     1803        lastdatafile = ''
     1804        newHistList = []
     1805#        lastVals = []
     1806        self.EnablePlot = False
     1807        for rd in rdlist:
     1808            if 'Instrument Parameters' in rd.pwdparms:
     1809                Iparm1,Iparm2 = rd.pwdparms['Instrument Parameters']
     1810            else:
     1811                # get instrument parameters for each dataset, unless already set
     1812#                if lastIparmfile:  # is this histogram like previous?
     1813#                    if lastVals != (rd.powderdata[0].min(),rd.powderdata[0].max(),len(rd.powderdata[0])):
     1814#                        lastIparmfile = ''
     1815                Iparms = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
     1816                if not Iparms:  #may have bailed out
     1817                    Id = 0
     1818                    continue
     1819                Iparm1,Iparm2 = Iparms
     1820                if rd.repeat_instparm:
     1821                    lastIparmfile = rd.instfile
     1822#                    lastVals = (rd.powderdata[0].min(),rd.powderdata[0].max(),len(rd.powderdata[0]))
     1823                # override any keys in read instrument parameters with ones set in import
     1824                for key in Iparm1: 
     1825                    if key in rd.instdict:
     1826                        Iparm1[key] = rd.instdict[key]
     1827            lastdatafile = rd.powderentry[0]
     1828            HistName = 'PWDR '+G2obj.StripUnicode(rd.idstring,'_')
     1829            # make new histogram names unique
     1830            if HistName in PWDRlist:
     1831                dlg = wx.MessageDialog(self,'Skip %s?'%(HistName),'Duplicate data name',wx.YES_NO)
     1832                try:
     1833                    if dlg.ShowModal() == wx.ID_YES:
     1834                        Id = 0
     1835                        continue
     1836                finally:
     1837                    dlg.Destroy()
     1838            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
     1839            print('Read powder data '+HistName+
     1840                ' from file '+rd.readfilename +
     1841                ' (format: '+ rd.formatName +
     1842                '). Inst parameters from '+rd.instmsg)
     1843            # data are read, now store them in the tree
     1844            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
     1845            if 'T' in Iparm1['Type'][0]:
     1846                if not rd.clockWd and rd.GSAS:
     1847                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
     1848                cw = np.diff(rd.powderdata[0])
     1849                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
     1850                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
     1851                    npts = min(len(rd.powderdata[0]),len(rd.powderdata[1]),len(cw))
     1852                    rd.powderdata[1] = rd.powderdata[1][:npts]/cw[:npts]
     1853                    rd.powderdata[2] = rd.powderdata[2][:npts]*cw[:npts]**2  #1/var=w at this point
     1854                else:       #NB: from topas/fullprof type files
     1855                    rd.powderdata[1] = rd.powderdata[1][:-1]
     1856                    rd.powderdata[2] = rd.powderdata[2][:-1]
     1857                if 'Itype' in Iparm2:
     1858                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
     1859                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
     1860                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
     1861                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
     1862                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
     1863                    var = 1./rd.powderdata[2][Ibeg:Ifin]
     1864                    var += WYI*rd.powderdata[1]**2
     1865                    var /= YI**2
     1866                    rd.powderdata[2] = 1./var
     1867                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])
     1868                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])
     1869                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])
     1870            Ymin = np.min(rd.powderdata[1])                 
     1871            Ymax = np.max(rd.powderdata[1])                 
     1872            valuesdict = {
     1873                'wtFactor':1.0,
     1874                'Dummy':False,
     1875                'ranId':ran.randint(0,sys.maxint),
     1876                'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
     1877                'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
     1878                }
     1879            # apply user-supplied corrections to powder data
     1880            if 'CorrectionCode' in Iparm1:
     1881                print('Applying corrections from instprm file')
     1882                corr = Iparm1['CorrectionCode'][0]
     1883                try:
     1884                    exec(corr)
     1885                    print('done')
     1886                except Exception as err:
     1887                    print(u'error: {}'.format(err))
     1888                    print('with commands -------------------')
     1889                    print(corr)
     1890                    print('---------------------------------')
     1891                finally:
     1892                    del Iparm1['CorrectionCode']
     1893            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
     1894            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
     1895            self.PatternTree.SetItemPyData(
     1896                self.PatternTree.AppendItem(Id,text='Comments'),
     1897                rd.comments)
     1898            Tmin = min(rd.powderdata[0])
     1899            Tmax = max(rd.powderdata[0])
     1900            Tmin1 = Tmin
     1901            if 'NT' in Iparm1['Type'][0] and G2lat.Pos2dsp(Iparm1,Tmin) < 0.4:               
     1902                Tmin1 = G2lat.Dsp2pos(Iparm1,0.4)
     1903            self.PatternTree.SetItemPyData(
     1904                self.PatternTree.AppendItem(Id,text='Limits'),
     1905                rd.pwdparms.get('Limits',[(Tmin,Tmax),[Tmin1,Tmax]])
     1906                )
     1907            self.PatternId = GetPatternTreeItemId(self,Id,'Limits')
     1908            self.PatternTree.SetItemPyData(
     1909                self.PatternTree.AppendItem(Id,text='Background'),
     1910                rd.pwdparms.get('Background',
     1911                    [['chebyschev',True,3,1.0,0.0,0.0],{'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
     1912                    )
     1913            self.PatternTree.SetItemPyData(
     1914                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
     1915                [Iparm1,Iparm2])
     1916            self.PatternTree.SetItemPyData(
     1917                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
     1918                rd.Sample)
     1919            self.PatternTree.SetItemPyData(
     1920                self.PatternTree.AppendItem(Id,text='Peak List')
     1921                ,{'peaks':[],'sigDict':{}})
     1922            self.PatternTree.SetItemPyData(
     1923                self.PatternTree.AppendItem(Id,text='Index Peak List'),
     1924                [[],[]])
     1925            self.PatternTree.SetItemPyData(
     1926                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
     1927                [])
     1928            self.PatternTree.SetItemPyData(
     1929                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
     1930                {})
     1931            # if any Control values have been set, move them into tree
     1932            Controls = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,self.root, 'Controls'))
     1933            Controls.update(rd.Controls)
     1934            newHistList.append(HistName)
     1935            rd.repeat_instparm = False  #clear the iparm reuse flag
     1936        else:
     1937            self.EnablePlot = True
     1938            if Id:
     1939                self.PatternTree.Expand(Id)
     1940                self.PatternTree.SelectItem(Id)
     1941
     1942        if not newHistList: return # somehow, no new histograms
     1943        # make a list of phase names
     1944        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
     1945        phaseNameList = usedHistograms.keys() # phase names in use
     1946        if not phaseNameList: return # no phases yet, nothing to do
     1947        header = 'Select phase(s) to link\nto the newly-read data:'
     1948        for Name in newHistList:
     1949            header += '\n  '+str(Name)
     1950
     1951        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
     1952        if not result: return
     1953        # connect new phases to histograms
     1954        sub = GetPatternTreeItemId(self,self.root,'Phases')
     1955        if not sub:
     1956            raise Exception('ERROR -- why are there no phases here?')
     1957        item, cookie = self.PatternTree.GetFirstChild(sub)
     1958        iph = -1
     1959        while item: # loop over (new) phases
     1960            iph += 1
     1961            data = self.PatternTree.GetItemPyData(item)
     1962            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     1963            if iph not in result: continue
     1964            generalData = data['General']
     1965            SGData = generalData['SGData']
     1966            UseList = data['Histograms']
     1967            NShkl = len(G2spc.MustrainNames(SGData))
     1968            NDij = len(G2spc.HStrainNames(SGData))
     1969            for histoName in newHistList:
     1970                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
     1971                Id = GetPatternTreeItemId(self,self.root,histoName)
     1972                refList = self.PatternTree.GetItemPyData(
     1973                    GetPatternTreeItemId(self,Id,'Reflection Lists'))
     1974                refList[generalData['Name']] = []
     1975        self.EnableRefineCommand()
     1976        return # success
     1977
     1978    def OnDummyPowder(self,event):
     1979        '''Called in response to Import/Powder Data/Simulate menu item
     1980        to create a Dummy powder diffraction data set.
     1981
     1982        Reads an instrument parameter file and then gets input from the user
     1983        '''
     1984        # get a list of existing histograms
     1985        PWDRlist = []
     1986        if self.PatternTree.GetCount():
     1987            item, cookie = self.PatternTree.GetFirstChild(self.root)
     1988            while item:
     1989                name = self.PatternTree.GetItemText(item)
     1990                if name.startswith('PWDR ') and name not in PWDRlist:
     1991                    PWDRlist.append(name)
     1992                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     1993        # Initialize a base class reader
     1994        rd = G2obj.ImportPowderData(
     1995            extensionlist=tuple(),
     1996            strictExtension=False,
     1997            formatName = 'Simulate dataset',
     1998            longFormatName = 'Compute a simulated pattern')
     1999        rd.powderentry[0] = '' # no filename
     2000        # #self.powderentry[1] = pos # bank offset (N/A here)
     2001        rd.powderentry[2] = 1 # only one bank
     2002        rd.comments.append('This is a dummy dataset for powder pattern simulation')
     2003        self.CheckNotebook()
     2004        Iparm = None
     2005        lastdatafile = ''
     2006        self.zipfile = None
     2007        # get instrument parameters for it
     2008        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, '', lastdatafile)
     2009        if 'T' in Iparm1['Type'][0]:
     2010            print('TOF simulation not supported yet')
     2011            return False
     2012        else:
     2013            # need to get name, 2theta start, end, step
     2014            rd.idstring = ' CW'
     2015            if 'X' in Iparm1['Type'][0]:
     2016                rd.idstring = 'CW x-ray simulation'
     2017            else:
     2018                rd.idstring = 'CW neutron simulation'
     2019            # base initial range on wavelength
     2020            wave = Iparm1.get('Lam')
     2021            if wave:
     2022                wave = wave[0]
     2023            else:
     2024                wave = Iparm1.get('Lam1')
     2025                if wave:
     2026                    wave = wave[0]
     2027        N = 0
     2028        while (N < 3): # insist on a dataset with a few points
     2029            names = ('dataset name', 'start angle', 'end angle', 'step size')
     2030            if not wave or wave < 1.0:
     2031                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
     2032            else:
     2033                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
     2034            dlg = G2G.ScrolledMultiEditor(
     2035                self,[inp] * len(inp),range(len(inp)),names,
     2036                header='Enter simulation name and range',
     2037                minvals=(None,0.001,0.001,0.0001),
     2038                maxvals=(None,180.,180.,.1),
     2039                sizevals=((225,-1),)
     2040                )
     2041            dlg.CenterOnParent()
     2042            if dlg.ShowModal() == wx.ID_OK:
     2043                if inp[1] > inp[2]:
     2044                    end,start,step = inp[1:]
     2045                else:               
     2046                    start,end,step = inp[1:]
     2047                step = abs(step)
     2048            else:
     2049                return False
     2050            N = int((end-start)/step)+1
     2051            x = np.linspace(start,end,N,True)
     2052            N = len(x)
     2053        rd.powderdata = [
     2054            np.array(x), # x-axis values
     2055            np.zeros_like(x), # powder pattern intensities
     2056            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
     2057            np.zeros_like(x), # calc. intensities (zero)
     2058            np.zeros_like(x), # calc. background (zero)
     2059            np.zeros_like(x), # obs-calc profiles
     2060            ]
     2061        Tmin = rd.powderdata[0][0]
     2062        Tmax = rd.powderdata[0][-1]
     2063        # data are read, now store them in the tree
     2064        HistName = inp[0]
     2065        HistName = 'PWDR '+HistName
     2066        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
     2067        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
     2068        Ymin = np.min(rd.powderdata[1])
     2069        Ymax = np.max(rd.powderdata[1])
     2070        valuesdict = {
     2071            'wtFactor':1.0,
     2072            'Dummy':True,
     2073            'ranId':ran.randint(0,sys.maxint),
     2074            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
     2075            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
     2076            }
     2077        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
     2078        self.PatternTree.SetItemPyData(
     2079            self.PatternTree.AppendItem(Id,text='Comments'),
     2080            rd.comments)
     2081        self.PatternTree.SetItemPyData(
     2082            self.PatternTree.AppendItem(Id,text='Limits'),
     2083            [(Tmin,Tmax),[Tmin,Tmax]])
     2084        self.PatternId = GetPatternTreeItemId(self,Id,'Limits')
     2085        self.PatternTree.SetItemPyData(
     2086            self.PatternTree.AppendItem(Id,text='Background'),
     2087            [['chebyschev',True,3,1.0,0.0,0.0],
     2088             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
     2089        self.PatternTree.SetItemPyData(
     2090            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
     2091            [Iparm1,Iparm2])
     2092        self.PatternTree.SetItemPyData(
     2093            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
     2094            rd.Sample)
     2095        self.PatternTree.SetItemPyData(
     2096            self.PatternTree.AppendItem(Id,text='Peak List')
     2097            ,{'peaks':[],'sigDict':{}})
     2098        self.PatternTree.SetItemPyData(
     2099            self.PatternTree.AppendItem(Id,text='Index Peak List'),
     2100            [[],[]])
     2101        self.PatternTree.SetItemPyData(
     2102            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
     2103            [])
     2104        self.PatternTree.SetItemPyData(
     2105            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
     2106            {})
     2107        self.PatternTree.Expand(Id)
     2108        self.PatternTree.SelectItem(Id)
     2109        print(u'Added simulation powder data {}'.format(HistName)+
     2110              ' with parameters from {}'.format(rd.instmsg))
     2111
     2112        # make a list of phase names
     2113        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
     2114        phaseNameList = usedHistograms.keys() # phase names in use
     2115        if not phaseNameList: return # no phases yet, nothing to do
     2116        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
     2117        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
     2118        if not result: return
     2119        # connect new phases to histograms
     2120        sub = GetPatternTreeItemId(self,self.root,'Phases')
     2121        if not sub:
     2122            raise Exception('ERROR -- why are there no phases here?')
     2123        item, cookie = self.PatternTree.GetFirstChild(sub)
     2124        iph = -1
     2125        while item: # loop over (new) phases
     2126            iph += 1
     2127            data = self.PatternTree.GetItemPyData(item)
     2128            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     2129            if iph not in result: continue
     2130            generalData = data['General']
     2131            SGData = generalData['SGData']
     2132            UseList = data['Histograms']
     2133            NShkl = len(G2spc.MustrainNames(SGData))
     2134            NDij = len(G2spc.HStrainNames(SGData))
     2135            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
     2136            Id = GetPatternTreeItemId(self,self.root,HistName)
     2137            refList = self.PatternTree.GetItemPyData(
     2138                GetPatternTreeItemId(self,Id,'Reflection Lists'))
     2139            refList[generalData['Name']] = []
     2140        self.EnableRefineCommand()
     2141        return # success
     2142       
     2143    def OnPreferences(self,event):
     2144        'Edit the GSAS-II configuration variables'
     2145        dlg = G2G.SelectConfigSetting(self)
     2146        dlg.ShowModal() == wx.ID_OK
     2147        dlg.Destroy()
     2148
     2149    def _Add_ImportMenu_smallangle(self,parent):
     2150        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
     2151        '''
     2152        submenu = wx.Menu()
     2153        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
     2154            submenu, help='Import small angle data')
     2155        for reader in self.ImportSmallAngleReaderlist:
     2156            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
     2157                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
     2158            self.ImportMenuId[item.GetId()] = reader
     2159            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
     2160        # item = submenu.Append(wx.ID_ANY,
     2161        #     help='Import small angle data, use file to try to determine format',
     2162        #     kind=wx.ITEM_NORMAL,text='guess format from file')
     2163        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
     2164
     2165    def OnImportSmallAngle(self,event):
     2166        '''Called in response to an Import/Small Angle Data/... menu item
     2167        to read a small angle diffraction data set.
     2168        dict self.ImportMenuId is used to look up the specific
     2169        reader item associated with the menu item, which will be
     2170        None for the last menu item, which is the "guess" option
     2171        where all appropriate formats will be tried.
     2172
     2173        '''
     2174       
     2175        def GetSASDIparm(reader):
     2176            parm = reader.instdict
     2177            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
     2178                parm['wave'],0],'Azimuth':[0.,0.,0]}           
     2179            return Iparm,{}
    11382180           
    1139     def Draw(self,Neigh,phase):
    1140         '''Creates the contents of the dialog. Normally called
    1141         by :meth:`__init__`.
     2181        # get a list of existing histograms
     2182        SASDlist = []
     2183        if self.PatternTree.GetCount():
     2184            item, cookie = self.PatternTree.GetFirstChild(self.root)
     2185            while item:
     2186                name = self.PatternTree.GetItemText(item)
     2187                if name.startswith('SASD ') and name not in SASDlist:
     2188                    SASDlist.append(name)
     2189                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     2190        # look up which format was requested
     2191        reqrdr = self.ImportMenuId.get(event.GetId()) 
     2192        rdlist = self.OnImportGeneric(
     2193            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
     2194        if len(rdlist) == 0: return
     2195        self.CheckNotebook()
     2196        newHistList = []
     2197        self.EnablePlot = False
     2198        for rd in rdlist:
     2199            HistName = rd.idstring
     2200            HistName = 'SASD '+HistName
     2201            # make new histogram names unique
     2202            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
     2203            print 'Read small angle data '+HistName+ \
     2204                ' from file '+self.lastimport
     2205            # data are read, now store them in the tree
     2206            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
     2207            Iparm1,Iparm2 = GetSASDIparm(rd)
     2208#            if 'T' in Iparm1['Type'][0]:
     2209#                if not rd.clockWd and rd.GSAS:
     2210#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
     2211#                cw = np.diff(rd.powderdata[0])
     2212#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
     2213#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
     2214#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
     2215#                if 'Itype' in Iparm2:
     2216#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
     2217#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
     2218#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
     2219#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
     2220#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
     2221#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
     2222#                    var += WYI*rd.powderdata[1]**2
     2223#                    var /= YI**2
     2224#                    rd.powderdata[2] = 1./var
     2225#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
     2226#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
     2227#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
     2228            Tmin = min(rd.smallangledata[0])
     2229            Tmax = max(rd.smallangledata[0])
     2230            valuesdict = {
     2231                'wtFactor':1.0,
     2232                'Dummy':False,
     2233                'ranId':ran.randint(0,sys.maxint),
     2234                'Offset':[0.0,0.0],
     2235                }
     2236            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
     2237            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
     2238            self.PatternTree.SetItemPyData(
     2239                self.PatternTree.AppendItem(Id,text='Comments'),
     2240                rd.comments)
     2241            self.PatternTree.SetItemPyData(
     2242                self.PatternTree.AppendItem(Id,text='Limits'),
     2243                [(Tmin,Tmax),[Tmin,Tmax]])
     2244            self.PatternId = GetPatternTreeItemId(self,Id,'Limits')
     2245            self.PatternTree.SetItemPyData(
     2246                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
     2247                [Iparm1,Iparm2])
     2248            self.PatternTree.SetItemPyData(
     2249                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
     2250            self.PatternTree.SetItemPyData(
     2251                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
     2252                rd.Sample)
     2253            self.PatternTree.SetItemPyData(
     2254                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
     2255            newHistList.append(HistName)
     2256        else:
     2257            self.EnablePlot = True
     2258            self.PatternTree.Expand(Id)
     2259            self.PatternTree.SelectItem(Id)
     2260           
     2261        if not newHistList: return # somehow, no new histograms
     2262        return # success
     2263       
     2264    def _Add_ImportMenu_reflectometry(self,parent):
     2265        '''configure the reflectometry Data menus accord to the readers found in _init_Imports
    11422266        '''
    1143         def OnHSelect(event):
    1144             Obj = event.GetEventObject()
    1145             item,i = Indx[Obj.GetId()]
    1146             for obj in Indx[item]:
    1147                 obj.SetValue(False)
    1148             Obj.SetValue(True)
    1149             self.Neigh[item][2] = i
     2267        submenu = wx.Menu()
     2268        item = parent.AppendMenu(wx.ID_ANY, 'Reflectometry Data',
     2269            submenu, help='Import reflectometry data')
     2270        for reader in self.ImportReflectometryReaderlist:
     2271            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
     2272                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
     2273            self.ImportMenuId[item.GetId()] = reader
     2274            self.Bind(wx.EVT_MENU, self.OnImportReflectometry, id=item.GetId())
     2275        # item = submenu.Append(wx.ID_ANY,
     2276        #     help='Import reflectometry data, use file to try to determine format',
     2277        #     kind=wx.ITEM_NORMAL,text='guess format from file')
     2278        # self.Bind(wx.EVT_MENU, self.OnImportReflectometry, id=item.GetId())
     2279       
     2280    def OnImportReflectometry(self,event):
     2281        '''Called in response to an Import/Reflectometry Data/... menu item
     2282        to read a reflectometry data set.
     2283        dict self.ImportMenuId is used to look up the specific
     2284        reader item associated with the menu item, which will be
     2285        None for the last menu item, which is the "guess" option
     2286        where all appropriate formats will be tried.
     2287
     2288        '''
     2289       
     2290        def GetREFDIparm(reader):
     2291            parm = reader.instdict
     2292            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
     2293                parm['wave'],0],'Azimuth':[0.,0.,0]}           
     2294            return Iparm,{}
    11502295           
    1151         def OnBond(event):
    1152             Obj = event.GetEventObject()
    1153             inei,ibond = Indx[Obj.GetId()]
    1154             self.Neigh[inei][1][0][ibond][2] = Obj.GetValue()
     2296        # get a list of existing histograms
     2297        REFDlist = []
     2298        if self.PatternTree.GetCount():
     2299            item, cookie = self.PatternTree.GetFirstChild(self.root)
     2300            while item:
     2301                name = self.PatternTree.GetItemText(item)
     2302                if name.startswith('REFD ') and name not in REFDlist:
     2303                    REFDlist.append(name)
     2304                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     2305        # look up which format was requested
     2306        reqrdr = self.ImportMenuId.get(event.GetId()) 
     2307        rdlist = self.OnImportGeneric(
     2308            reqrdr,self.ImportReflectometryReaderlist,'Reflectometry Data',multiple=True)
     2309        if len(rdlist) == 0: return
     2310        self.CheckNotebook()
     2311        newHistList = []
     2312        self.EnablePlot = False
     2313        for rd in rdlist:
     2314            HistName = rd.idstring
     2315            HistName = 'REFD '+HistName
     2316            # make new histogram names unique
     2317            HistName = G2obj.MakeUniqueLabel(HistName,REFDlist)
     2318            print 'Read reflectometry data '+HistName+ \
     2319                ' from file '+self.lastimport
     2320            # data are read, now store them in the tree
     2321            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
     2322            Iparm1,Iparm2 = GetREFDIparm(rd)
     2323#            if 'T' in Iparm1['Type'][0]:
     2324#                if not rd.clockWd and rd.GSAS:
     2325#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
     2326#                cw = np.diff(rd.powderdata[0])
     2327#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
     2328#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
     2329#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
     2330#                if 'Itype' in Iparm2:
     2331#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
     2332#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
     2333#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
     2334#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
     2335#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
     2336#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
     2337#                    var += WYI*rd.powderdata[1]**2
     2338#                    var /= YI**2
     2339#                    rd.powderdata[2] = 1./var
     2340#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
     2341#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
     2342#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
     2343            Tmin = min(rd.reflectometrydata[0])
     2344            Tmax = max(rd.reflectometrydata[0])
     2345            ifDQ = np.any(rd.reflectometrydata[5])
     2346            valuesdict = {
     2347                'wtFactor':1.0,
     2348                'Dummy':False,
     2349                'ranId':ran.randint(0,sys.maxint),
     2350                'Offset':[0.0,0.0],
     2351                'ifDQ':ifDQ
     2352                }
     2353            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
     2354            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.reflectometrydata])
     2355            self.PatternTree.SetItemPyData(
     2356                self.PatternTree.AppendItem(Id,text='Comments'),
     2357                rd.comments)
     2358            self.PatternTree.SetItemPyData(
     2359                self.PatternTree.AppendItem(Id,text='Limits'),
     2360                [(Tmin,Tmax),[Tmin,Tmax]])
     2361            self.PatternId = GetPatternTreeItemId(self,Id,'Limits')
     2362            self.PatternTree.SetItemPyData(
     2363                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
     2364                [Iparm1,Iparm2])
     2365            self.PatternTree.SetItemPyData(
     2366                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
     2367            self.PatternTree.SetItemPyData(
     2368                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
     2369                rd.Sample)
     2370            self.PatternTree.SetItemPyData(
     2371                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultREFDModel())
     2372            newHistList.append(HistName)
     2373        else:
     2374            self.EnablePlot = True
     2375            self.PatternTree.Expand(Id)
     2376            self.PatternTree.SelectItem(Id)
    11552377           
    1156         self.panel.Destroy()
    1157         self.panel = wxscroll.ScrolledPanel(self,style = wx.DEFAULT_DIALOG_STYLE)
    1158         mainSizer = wx.BoxSizer(wx.VERTICAL)
    1159         mainSizer.Add(wx.StaticText(self.panel,-1,'H atom add controls for phase %s:'%(phase['General']['Name'])),
    1160             0,wx.LEFT|wx.TOP,10)
    1161         mainSizer.Add(wx.StaticText(self.panel,-1,'NB: Check selections as they may not be correct'),0,WACV|wx.LEFT,10)
    1162         mainSizer.Add(wx.StaticText(self.panel,-1," Atom:  Add # H's          Use: Neighbors, dist"),0,wx.TOP|wx.LEFT,5)
    1163         nHatms = ['0','1','2','3']
    1164         dataSizer = wx.FlexGridSizer(0,3,0,0)
    1165         Indx = {}
    1166         for inei,neigh in enumerate(Neigh):
    1167             dataSizer.Add(wx.StaticText(self.panel,-1,' %s:  '%(neigh[0])),0,WACV)
    1168             nH = 1      #for O atom
    1169             if 'C' in neigh[0] or 'N' in neigh[0]:
    1170                 nH = 4-len(neigh[1][0])
    1171             checks = wx.BoxSizer(wx.HORIZONTAL)
    1172             Ids = []
    1173             for i in range(nH+1):
    1174                 nHs = wx.CheckBox(self.panel,-1,label=nHatms[i])
    1175                 if i == neigh[2]:
    1176                     nHs.SetValue(True)
    1177                 Indx[nHs.GetId()] = [inei,i]
    1178                 Ids.append(nHs)
    1179                 nHs.Bind(wx.EVT_CHECKBOX, OnHSelect)
    1180                 checks.Add(nHs,0,WACV)
    1181             Indx[inei] = Ids
    1182             dataSizer.Add(checks,0,WACV)
    1183             lineSizer = wx.BoxSizer(wx.HORIZONTAL)
    1184             for ib,bond in enumerate(neigh[1][0]):
    1185                 Bond = wx.CheckBox(self.panel,-1,label=': %s, %.3f'%(bond[0],bond[1]))
    1186                 Bond.SetValue(bond[2])
    1187                 Indx[Bond.GetId()] = [inei,ib]
    1188                 Bond.Bind(wx.EVT_CHECKBOX,OnBond)               
    1189                 lineSizer.Add(Bond,0,WACV)               
    1190             dataSizer.Add(lineSizer,0,WACV|wx.RIGHT,10)
    1191         mainSizer.Add(dataSizer,0,wx.LEFT,5)
    1192 
    1193         CancelBtn = wx.Button(self.panel,-1,'Cancel')
    1194         CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
    1195         OkBtn = wx.Button(self.panel,-1,'Ok')
    1196         OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
    1197         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
    1198         btnSizer.Add((20,20),1)
    1199         btnSizer.Add(OkBtn)
    1200         btnSizer.Add((20,20),1)
    1201         btnSizer.Add(CancelBtn)
    1202         btnSizer.Add((20,20),1)
    1203         mainSizer.Add(btnSizer,0,wx.BOTTOM|wx.TOP, 10)
    1204         self.panel.SetSizer(mainSizer)
    1205         size = np.array(self.GetSize())
    1206         self.panel.SetupScrolling()
    1207         self.panel.SetAutoLayout(1)
    1208         size = [size[0]-5,size[1]-20]       #this fiddling is needed for older wx!
    1209         self.panel.SetSize(size)
    1210        
    1211     def GetData(self):
    1212         'Returns the values from the dialog'
    1213         for neigh in self.Neigh:
    1214             for ibond,bond in enumerate(neigh[1][0]):
    1215                 if not bond[2]:
    1216                     neigh[1][1][1][ibond] = 0   #deselected bond
    1217             neigh[1][1][1] = [a for a in  neigh[1][1][1] if a]
    1218         return self.Neigh       #has #Hs to add for each entry
    1219        
    1220     def OnOk(self,event):
    1221         'Called when the OK button is pressed'
    1222         parent = self.GetParent()
    1223         parent.Raise()
    1224         self.EndModal(wx.ID_OK)             
    1225 
    1226     def OnCancel(self,event):
    1227         parent = self.GetParent()
    1228         parent.Raise()
    1229         self.EndModal(wx.ID_CANCEL)
     2378        if not newHistList: return # somehow, no new histograms
     2379        return # success
     2380
     2381    def _Add_ImportMenu_PDF(self,parent):
     2382        '''configure the PDF Data menus accord to the readers found in _init_Imports
     2383        '''
     2384        submenu = wx.Menu()
     2385        item = parent.AppendMenu(wx.ID_ANY, 'PDF G(R) Data',
     2386            submenu, help='Import PDF G(R) data')
     2387        for reader in self.ImportPDFReaderlist:
     2388            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
     2389                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
     2390            self.ImportMenuId[item.GetId()] = reader
     2391            self.Bind(wx.EVT_MENU, self.OnImportPDF, id=item.GetId())
     2392        # item = submenu.Append(wx.ID_ANY,
     2393        #     help='Import reflectometry data, use file to try to determine format',
     2394        #     kind=wx.ITEM_NORMAL,text='guess format from file')
     2395        # self.Bind(wx.EVT_MENU, self.OnImportReflectometry, id=item.GetId())
     2396       
     2397    def OnImportPDF(self,event):
     2398        '''Called in response to an Import/PDF G(R) Data/... menu item
     2399        to read a PDF G(R) data set.
     2400        dict self.ImportMenuId is used to look up the specific
     2401        reader item associated with the menu item, which will be
     2402        None for the last menu item, which is the "guess" option
     2403        where all appropriate formats will be tried.
     2404
     2405        '''
     2406       
     2407        # get a list of existing histograms
     2408        PDFlist = []
     2409        if self.PatternTree.GetCount():
     2410            item, cookie = self.PatternTree.GetFirstChild(self.root)
     2411            while item:
     2412                name = self.PatternTree.GetItemText(item)
     2413                if name.startswith('PDF ') and name not in PDFlist:
     2414                    PDFlist.append(name)
     2415                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     2416        # look up which format was requested
     2417        reqrdr = self.ImportMenuId.get(event.GetId()) 
     2418        rdlist = self.OnImportGeneric(
     2419            reqrdr,self.ImportPDFReaderlist,'PDF G(R) Data',multiple=True)
     2420        if len(rdlist) == 0: return
     2421        self.CheckNotebook()
     2422        newHistList = []
     2423        self.EnablePlot = False
     2424        for rd in rdlist:
     2425            HistName = rd.idstring
     2426            HistName = 'PDF '+HistName
     2427            # make new histogram names unique
     2428            HistName = G2obj.MakeUniqueLabel(HistName,PDFlist)
     2429            print 'Read PDF G(R) data '+HistName+ \
     2430                ' from file '+self.lastimport
     2431            # data are read, now store them in the tree
     2432            Id = self.PatternTree.AppendItem(self.root,text=HistName)
     2433            Ymin = np.min(rd.pdfdata[1])                 
     2434            Ymax = np.max(rd.pdfdata[1])                 
     2435            valuesdict = {
     2436                'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),
     2437                'Offset':[0.0,0.0],'delOffset':0.02*Ymax,
     2438                'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
     2439                }
     2440            self.PatternTree.SetItemPyData(
     2441                self.PatternTree.AppendItem(Id,text='PDF Controls'),
     2442                    {'G(R)':[valuesdict,rd.pdfdata,HistName],'diffGRname':'','diffMult':1.0})
     2443            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Peaks'),
     2444                {'Limits':[1.,5.],'Background':[2,[0.,-0.2*np.pi],False],'Peaks':[]})
     2445        else:
     2446            self.EnablePlot = True
     2447            self.PatternTree.Expand(Id)
     2448            self.PatternTree.SelectItem(Id)
     2449           
     2450        if not newHistList: return # somehow, no new histograms
     2451        return # success
     2452       
     2453###############################################################################
     2454#Command logging
     2455###############################################################################
     2456
     2457    def OnMacroRecordStatus(self,event,setvalue=None):
     2458        '''Called when the record macro menu item is used which toggles the
     2459        value. Alternately a value to be set can be provided. Note that this
     2460        routine is made more complex because on the Mac there are lots of menu
     2461        items (listed in self.MacroStatusList) and this loops over all of them.
     2462        '''
     2463        nextvalue = log.ShowLogStatus() != True
     2464        if setvalue is not None:
     2465            nextvalue = setvalue
     2466        if nextvalue:
     2467            log.LogOn()
     2468            set2 = True
     2469        else:
     2470            log.LogOff()
     2471            set2 = False
     2472        for menuitem in self.MacroStatusList:
     2473            menuitem.Check(set2)
     2474
     2475    def _init_Macro(self):
     2476        '''Define the items in the macro menu.
     2477        '''
     2478        menu = self.MacroMenu
     2479        item = menu.Append(
     2480                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
     2481                kind=wx.ITEM_CHECK,text='Record actions')
     2482        self.MacroStatusList.append(item)
     2483        item.Check(log.ShowLogStatus())
     2484        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
     2485
     2486        # this may only be of value for development work
     2487        item = menu.Append(
     2488            help='Show logged commands', id=wx.ID_ANY,
     2489            kind=wx.ITEM_NORMAL,text='Show log')
     2490        def OnShowLog(event):
     2491            print 70*'='
     2492            print 'List of logged actions'
     2493            for i,line in enumerate(log.G2logList):
     2494                if line: print i,line
     2495            print 70*'='
     2496        self.Bind(wx.EVT_MENU, OnShowLog, item)
     2497
     2498        item = menu.Append(
     2499            help='Clear logged commands', id=wx.ID_ANY,
     2500            kind=wx.ITEM_NORMAL,text='Clear log')
     2501        def OnClearLog(event): log.G2logList=[None]
     2502        self.Bind(wx.EVT_MENU, OnClearLog, item)
     2503       
     2504        item = menu.Append(
     2505            help='Save logged commands to file', id=wx.ID_ANY,
     2506            kind=wx.ITEM_NORMAL,text='Save log')
     2507        def OnSaveLog(event):
     2508            import cPickle
     2509            defnam = os.path.splitext(
     2510                os.path.split(self.GSASprojectfile)[1]
     2511                )[0]+'.gcmd'
     2512            dlg = wx.FileDialog(self,
     2513                'Choose an file to save past actions', '.', defnam,
     2514                'GSAS-II cmd output (*.gcmd)|*.gcmd',
     2515                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
     2516            dlg.CenterOnParent()
     2517            try:
     2518                if dlg.ShowModal() == wx.ID_OK:
     2519                    filename = dlg.GetPath()
     2520                    # make sure extension is correct
     2521                    filename = os.path.splitext(filename)[0]+'.gcmd'
     2522                else:
     2523                    filename = None
     2524            finally:
     2525                dlg.Destroy()
     2526            if filename:
     2527                fp = open(filename,'wb')
     2528                fp.write(str(len(log.G2logList)-1)+'\n')
     2529                for item in log.G2logList:
     2530                    if item: cPickle.dump(item,fp)
     2531                fp.close()
     2532        self.Bind(wx.EVT_MENU, OnSaveLog, item)
     2533
     2534        item = menu.Append(
     2535            help='Load logged commands from file', id=wx.ID_ANY,
     2536            kind=wx.ITEM_NORMAL,text='Load log')
     2537        def OnLoadLog(event):
     2538            # this appends. Perhaps we should ask to clear?
     2539            import cPickle
     2540            defnam = os.path.splitext(
     2541                os.path.split(self.GSASprojectfile)[1]
     2542                )[0]+'.gcmd'
     2543            dlg = wx.FileDialog(self,
     2544                'Choose an file to read saved actions', '.', defnam,
     2545                'GSAS-II cmd output (*.gcmd)|*.gcmd',
     2546                wx.OPEN)
     2547            dlg.CenterOnParent()
     2548            try:
     2549                if dlg.ShowModal() == wx.ID_OK:
     2550                    filename = dlg.GetPath()
     2551                    # make sure extension is correct
     2552                    filename = os.path.splitext(filename)[0]+'.gcmd'
     2553                else:
     2554                    filename = None
     2555            finally:
     2556                dlg.Destroy()
     2557            if filename and os.path.exists(filename):
     2558                fp = open(filename,'rb')
     2559                lines = fp.readline()
     2560                for i in range(int(lines)):
     2561                    log.G2logList.append(cPickle.load(fp))
     2562                fp.close()
     2563        self.Bind(wx.EVT_MENU, OnLoadLog, item)
     2564
     2565        item = menu.Append(
     2566            help='Replay saved commands', id=wx.ID_ANY,
     2567            kind=wx.ITEM_NORMAL,text='Replay log')
     2568        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
     2569
     2570    def _init_Exports(self,menu):
     2571        '''Find exporter routines and add them into menus
     2572        '''
     2573        # set up the top-level menus
     2574        projectmenu = wx.Menu()
     2575        item = menu.AppendMenu(
     2576            wx.ID_ANY, 'Entire project as',
     2577            projectmenu, help='Export entire project')
     2578
     2579        phasemenu = wx.Menu()
     2580        item = menu.AppendMenu(
     2581            wx.ID_ANY, 'Phase as',
     2582            phasemenu, help='Export phase or sometimes phases')
     2583
     2584        powdermenu = wx.Menu()
     2585        item = menu.AppendMenu(
     2586            wx.ID_ANY, 'Powder data as',
     2587            powdermenu, help='Export powder diffraction histogram(s)')
     2588
     2589        singlemenu = wx.Menu()
     2590        item = menu.AppendMenu(
     2591            wx.ID_ANY, 'Single crystal data as',
     2592            singlemenu, help='Export single crystal histogram(s)')
     2593
     2594        imagemenu = wx.Menu()
     2595        item = menu.AppendMenu(
     2596            wx.ID_ANY, 'Image data as',
     2597            imagemenu, help='Export powder image(s) data')
     2598
     2599        mapmenu = wx.Menu()
     2600        item = menu.AppendMenu(
     2601            wx.ID_ANY, 'Maps as',
     2602            mapmenu, help='Export density map(s)')
     2603
     2604        # pdfmenu = wx.Menu()
     2605        # item = menu.AppendMenu(
     2606        #     wx.ID_ANY, 'PDFs as',
     2607        #     pdfmenu, help='Export pair distribution function(s)')
     2608
     2609        # find all the exporter files
     2610        if not self.exporterlist: # this only needs to be done once
     2611            pathlist = sys.path
     2612            filelist = []
     2613            for path in pathlist:
     2614                for filename in glob.iglob(os.path.join(path,"G2export*.py")):
     2615                    filelist.append(filename)   
     2616            filelist = sorted(list(set(filelist))) # remove duplicates
     2617            # go through the routines and import them, saving objects that
     2618            # have export routines (method Exporter)
     2619            for filename in filelist:
     2620                path,rootname = os.path.split(filename)
     2621                pkg = os.path.splitext(rootname)[0]
     2622#                try:
     2623                fp = None
     2624                fp, fppath,desc = imp.find_module(pkg,[path,])
     2625                pkg = imp.load_module(pkg,fp,fppath,desc)
     2626                for clss in inspect.getmembers(pkg): # find classes defined in package
     2627                    if clss[0].startswith('_'): continue
     2628                    if inspect.isclass(clss[1]):
     2629                        # check if we have the required methods
     2630                        for m in 'Exporter','loadParmDict':
     2631                            if not hasattr(clss[1],m): break
     2632                            if not callable(getattr(clss[1],m)): break
     2633                        else:
     2634                            exporter = clss[1](self) # create an export instance
     2635                            self.exporterlist.append(exporter)
     2636#                except AttributeError:
     2637#                    print 'Import_'+errprefix+': Attribute Error'+str(filename)
     2638#                    pass
     2639#                except ImportError:
     2640#                    print 'Import_'+errprefix+': Error importing file'+str(filename)
     2641#                    pass
     2642                if fp: fp.close()
     2643        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
     2644        for obj in self.exporterlist:
     2645            #print 'exporter',obj
     2646            for typ in obj.exporttype:
     2647                if typ == "project":
     2648                    submenu = projectmenu
     2649                elif typ == "phase":
     2650                    submenu = phasemenu
     2651                elif typ == "powder":
     2652                    submenu = powdermenu
     2653                elif typ == "single":
     2654                    submenu = singlemenu
     2655                elif typ == "image":
     2656                    submenu = imagemenu
     2657                elif typ == "map":
     2658                    submenu = mapmenu
     2659                # elif typ == "pdf":
     2660                #     submenu = pdfmenu
     2661                else:
     2662                    print("Error, unknown type in "+str(obj))
     2663                    break
     2664                item = submenu.Append(
     2665                    wx.ID_ANY,
     2666                    help=obj.longFormatName,
     2667                    kind=wx.ITEM_NORMAL,
     2668                    text=obj.formatName)
     2669                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
     2670                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
     2671        item = imagemenu.Append(wx.ID_ANY,
     2672                        help='Export image controls and masks for multiple images',
     2673                        kind=wx.ITEM_NORMAL,
     2674                        text='Multiple image controls and masks')
     2675        self.Bind(wx.EVT_MENU, self.OnSaveMultipleImg, id=item.GetId())
     2676        #code to debug an Exporter. hard-code the routine below, to allow a reload before use
     2677        # def DebugExport(event):
     2678        #      print 'start reload'
     2679        #      reload(G2IO)
     2680        #      import G2export_pwdr as dev
     2681        #      reload(dev)
     2682        #      dev.ExportPowderFXYE(self).Exporter(event)
     2683        # item = menu.Append(
     2684        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
     2685        #     help="debug exporter",text="test Export FXYE")
     2686        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
     2687        # # #self.ExportLookup[item.GetId()] = 'image'
     2688        # self.ExportLookup[item.GetId()] = 'powder'
     2689
     2690###############################################################################
     2691# Exporters
     2692###############################################################################
     2693                           
     2694    def _Add_ExportMenuItems(self,parent):
     2695        # item = parent.Append(
     2696        #     help='Select PWDR item to enable',id=wx.ID_ANY,
     2697        #     kind=wx.ITEM_NORMAL,
     2698        #     text='Export Powder Patterns...')
     2699        # self.ExportPattern.append(item)
     2700        # item.Enable(False)
     2701        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
     2702
     2703        item = parent.Append(
     2704            help='',id=wx.ID_ANY,
     2705            kind=wx.ITEM_NORMAL,
     2706            text='Export All Peak Lists...')
     2707        self.ExportPeakList.append(item)
     2708        item.Enable(True)
     2709        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
     2710
     2711        item = parent.Append(
     2712            help='',id=wx.ID_ANY,
     2713            kind=wx.ITEM_NORMAL,
     2714            text='Export HKLs...')
     2715        self.ExportHKL.append(item)
     2716        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
     2717
     2718        item = parent.Append(
     2719            help='Select PDF item to enable',
     2720            id=wx.ID_ANY,
     2721            kind=wx.ITEM_NORMAL,
     2722            text='Export PDF...')
     2723        self.ExportPDF.append(item)
     2724        item.Enable(False)
     2725        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
     2726           
     2727    def FillMainMenu(self,menubar,addhelp=True):
     2728        '''Define contents of the main GSAS-II menu for the (main) data tree window.
     2729        For the mac, this is also called for the data item windows as well so that
     2730        the main menu items are data menu as well.
     2731        '''
     2732        File = wx.Menu(title='')
     2733        menubar.Append(menu=File, title='&File')
     2734        self._Add_FileMenuItems(File)
     2735        Data = wx.Menu(title='')
     2736        menubar.Append(menu=Data, title='Data')
     2737        self._Add_DataMenuItems(Data)
     2738        Calculate = wx.Menu(title='')       
     2739        menubar.Append(menu=Calculate, title='&Calculate')
     2740        self._Add_CalculateMenuItems(Calculate)
     2741        Import = wx.Menu(title='')       
     2742        menubar.Append(menu=Import, title='Import')
     2743        self._Add_ImportMenu_Image(Import)
     2744        self._Add_ImportMenu_Phase(Import)
     2745        self._Add_ImportMenu_powder(Import)
     2746        self._Add_ImportMenu_Sfact(Import)
     2747        self._Add_ImportMenu_smallangle(Import)
     2748        self._Add_ImportMenu_reflectometry(Import)
     2749        self._Add_ImportMenu_PDF(Import)
     2750
     2751        #======================================================================
     2752        # Code to help develop/debug an importer, much is hard-coded below
     2753        # but module is reloaded before each use, allowing faster testing
     2754        # def DebugImport(event):
     2755        #     print 'start reload'
     2756        #     import G2phase_ISO as dev
     2757        #     reload(dev)
     2758        #     rd = dev.ISODISTORTPhaseReader()
     2759        #     self.ImportMenuId[event.GetId()] = rd
     2760        #     self.OnImportPhase(event)
     2761            # or ----------------------------------------------------------------------
     2762            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
     2763            # special debug code
     2764            # or ----------------------------------------------------------------------
     2765            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
     2766            # fp = open(filename,'Ur')
     2767            # if not rd.ContentsValidator(fp):
     2768            #     print 'not validated'
     2769            #     # make a list of used phase ranId's
     2770            # phaseRIdList = []
     2771            # sub = GetPatternTreeItemId(self,self.root,'Phases')
     2772            # if sub:
     2773            #     item, cookie = self.PatternTree.GetFirstChild(sub)
     2774            #     while item:
     2775            #         phaseName = self.PatternTree.GetItemText(item)
     2776            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
     2777            #         if ranId: phaseRIdList.append(ranId)
     2778            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     2779            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
     2780            #     print 'read OK'
     2781        # item = Import.Append(
     2782        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
     2783        #     help="debug importer",text="test importer")
     2784        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
     2785        #======================================================================
     2786        self.ExportMenu = wx.Menu(title='')
     2787        menubar.Append(menu=self.ExportMenu, title='Export')
     2788        self._init_Exports(self.ExportMenu)
     2789        self._Add_ExportMenuItems(self.ExportMenu)
     2790        if GSASIIpath.GetConfigValue('Enable_logging'):
     2791            self.MacroMenu = wx.Menu(title='')
     2792            menubar.Append(menu=self.MacroMenu, title='Macro')
     2793            self._init_Macro()
     2794        if addhelp:
     2795            HelpMenu=G2G.MyHelp(self,includeTree=True,
     2796                morehelpitems=[('&Tutorials','Tutorials'),])
     2797            menubar.Append(menu=HelpMenu,title='&Help')
     2798           
     2799    def _init_ctrls(self, parent):
     2800        wx.Frame.__init__(self, name='GSASII', parent=parent,
     2801            size=wx.Size(700, 450),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
     2802            #size=wx.Size(400, 450),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
     2803        clientSize = wx.ClientDisplayRect()
     2804        Size = self.GetSize()
     2805        xPos = clientSize[2]-Size[0]
     2806        self.SetPosition(wx.Point(xPos,clientSize[1]))
     2807        self._init_Imports()
     2808        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
     2809        self.MakePDF = []
     2810        self.Refine = []
     2811        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
     2812        #self.ExportPattern = []
     2813        self.ExportPeakList = []
     2814        self.ExportHKL = []
     2815        self.ExportPDF = []
     2816        self.ExportPhase = []
     2817        self.ExportCIF = []
     2818        #
     2819        self.GSASIIMenu = wx.MenuBar()
     2820        # create a list of all dataframe menus (appended in PrefillDataMenu)
     2821        self.dataMenuBars = [self.GSASIIMenu]
     2822        self.MacroStatusList = []
     2823        self.FillMainMenu(self.GSASIIMenu)
     2824        self.SetMenuBar(self.GSASIIMenu)
     2825        #self.Bind(wx.EVT_SIZE, self.OnSize)
     2826        self.Status = self.CreateStatusBar()
     2827        #self.mainPanel = GSASIIsplit(self,-1)
     2828        self.mainPanel = wx.SplitterWindow(self, wx.ID_ANY, style=wx.SP_BORDER|wx.SP_LIVE_UPDATE)
     2829        self.mainPanel.SetMinimumPaneSize(100)
     2830        self.treePanel = wx.Panel(self.mainPanel, wx.ID_ANY,
     2831            style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
     2832        #self.dataPanel = wx.Panel(self.mainPanel, wx.ID_ANY,
     2833        #    style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
     2834        #self.mainPanel.SplitVertically(self.treePanel, self.dataPanel, 200)
     2835       
     2836        self.dataWindow = DataWindow(self.mainPanel)
     2837        #self.dataWindow = wxscroll.ScrolledPanel(self.mainPanel,style=wx.BORDER_SUNKEN)
     2838        self.mainPanel.SplitVertically(self.treePanel, self.dataWindow, 200)
     2839       
     2840        wxID_PATTERNTREE = wx.NewId()
     2841        treeSizer = wx.BoxSizer()
     2842        self.treePanel.SetSizer(treeSizer)
     2843        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
     2844            parent=self.treePanel, size=self.treePanel.GetClientSize(),style=wx.TR_DEFAULT_STYLE )
     2845        treeSizer.Add(self.PatternTree,1,wx.EXPAND|wx.ALL,0)
     2846        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,self.OnDataTreeSelChanged)
     2847        self.PatternTree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK,self.OnDataTreeSelChanged)
     2848        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
     2849            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
     2850        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
     2851            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
     2852        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
     2853            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
     2854        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
     2855            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
     2856        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
     2857            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
     2858        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
     2859            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
     2860        self.root = self.PatternTree.root       
     2861       
     2862        # def FillWindow(panel,sizer,size=1.):
     2863        #     sizer.Add(wx.StaticText(panel, wx.ID_ANY, "Panel Two: long line "+int(size*40)*'*', (5,5)))
     2864        #     for i in range(int(size*15)): sizer.Add(wx.StaticText(panel, wx.ID_ANY, "Line "+str(2+i), (5,5)))
     2865
     2866        # dataSizer = wx.BoxSizer(wx.VERTICAL)
     2867        # FillWindow(self.dataWindow,dataSizer)
     2868        # self.dataWindow.SetSizer(dataSizer)
     2869        #self.dataWindow = DataWindow(self.dataPanel)
     2870        #self.dataWindow.SetSize(self.dataPanel.GetClientSize())
     2871        #self.dataWindow.SetInitialSize()
     2872        self.dataWindow.SetupScrolling()
     2873        self.dataFrame = self.dataWindow        #kluge!!
     2874#        self.SetDataSize()
     2875       
     2876       
     2877        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
     2878            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
     2879        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame,G2frame=self)
     2880        plotFrame.Show()
     2881       
     2882    def __init__(self, parent):
     2883        self.ExportLookup = {}
     2884        self.exporterlist = []
     2885        self._init_ctrls(parent)
     2886        self.Image = wx.Image(
     2887            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
     2888            wx.BITMAP_TYPE_ICO)
     2889        if "wxMSW" in wx.PlatformInfo:
     2890            img = self.Image.Scale(16, 16).ConvertToBitmap()
     2891        elif "wxGTK" in wx.PlatformInfo:
     2892            img = self.Image.Scale(22, 22).ConvertToBitmap()
     2893        else:
     2894            img = self.Image.ConvertToBitmap()
     2895        self.SetIcon(wx.IconFromBitmap(img))
     2896        self.Bind(wx.EVT_CLOSE, self.ExitMain)
     2897        # various defaults
     2898        self.oldFocus = None
     2899        self.GSASprojectfile = ''
     2900        self.undofile = ''
     2901        self.TreeItemDelete = False
     2902        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False,'sqPlot':False}
     2903        self.Weight = False
     2904        self.IfPlot = False
     2905        self.DDShowAll = False
     2906        self.atmSel = ''
     2907        self.PatternId = 0
     2908        self.PickId = 0
     2909        self.PickIdText = None
     2910        self.PeakTable = []
     2911        self.LimitsTable = []
     2912        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
     2913        self.HKL = []
     2914        self.Lines = []
     2915        self.itemPicked = None
     2916        self.Interpolate = 'nearest'
     2917        self.ContourColor = GSASIIpath.GetConfigValue('Contour_color','Paired')
     2918        self.VcovColor = 'RdYlGn'
     2919        self.RamaColor = 'Blues'
     2920        self.Projection = 'equal area'
     2921        self.logPlot = False
     2922        self.plusPlot = True
     2923        self.ErrorBars = False
     2924        self.Contour = False
     2925        self.Legend = False
     2926        self.SinglePlot = True
     2927        self.Waterfall = False
     2928        self.selections= None
     2929        self.PDFselections = None
     2930        self.SubBack = False
     2931        self.seqReverse = False
     2932        self.seqLines = True #draw lines between points
     2933        self.plotView = 0
     2934        self.Image = 0
     2935        self.oldImagefile = '' # the name of the last image file read
     2936        self.oldImageTag = None # the name of the tag for multi-image files
     2937        self.PauseIntegration = False
     2938        self.ImageZ = []
     2939        self.Integrate = 0
     2940        self.imageDefault = {}
     2941        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
     2942        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
     2943        self.autoIntFrame = None
     2944        self.IntegratedList = [] # list of already integrated IMG tree items
     2945        self.Sngl = False
     2946        self.ifGetRing = False
     2947        self.MaskKey = ''           #trigger for making image masks
     2948        self.MskDelete = False      #trigger for mask delete
     2949        self.StrainKey = ''         #ditto for new strain d-zeros
     2950        self.EnablePlot = True
     2951        self.hist = ''              # selected histogram in Phase/Data tab
     2952        self.dirname = os.path.abspath(os.path.expanduser('~'))       #start in the users home directory by default; may be meaningless
     2953        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
     2954        self.LastImportDir = None # last-used directory where an import was done
     2955        self.LastGPXdir = None    # directory where a GPX file was last read
     2956        self.LastExportDir = None  # the last directory used for exports, if any.
     2957        self.dataDisplayPhaseText = ''
     2958        self.lastTreeSetting = []
     2959        self.ExpandingAll = False
     2960        self.SeqTblHideList = []
     2961               
     2962        arg = sys.argv
     2963        if len(arg) > 1 and arg[1]:
     2964            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
     2965            self.dirname = os.path.abspath(os.path.dirname(arg[1]))
     2966            if self.dirname: os.chdir(self.dirname)
     2967            try:
     2968                self.StartProject()         #open the file if possible
     2969                return
     2970            except Exception:
     2971                print 'Error opening or reading file',arg[1]
     2972                import traceback
     2973                print traceback.format_exc()
     2974               
     2975        if GSASIIpath.GetConfigValue('Starting_directory'):
     2976            try:
     2977                pth = GSASIIpath.GetConfigValue('Starting_directory')
     2978                pth = os.path.expanduser(pth)
     2979                os.chdir(pth)
     2980                self.LastGPXdir = pth
     2981            except:
     2982                print('Ignoring Config Starting_directory value: '+
     2983                      GSASIIpath.GetConfigValue('Starting_directory'))
     2984
     2985    def GetTreeItemsList(self,item):
     2986        return self.PatternTree._getTreeItemsList(item)
     2987
     2988    # def OnSize(self,event):
     2989    #     'Called to make PatternTree fill mainPanel'
     2990    #     print 'OnSize'
     2991    #     event.Skip()
     2992        # w,h = self.GetClientSizeTuple()
     2993        # self.dataWindow.SetupScrolling()
     2994        # self.mainPanel.SetSize(wx.Size(w,h))
     2995        # self.PatternTree.SetSize(wx.Size(w,h))
     2996        # self.dataWindow.SetSize(self.dataPanel.GetClientSize())
     2997       
     2998    def SetDataSize(self):
     2999        self.dataWindow.Layout()
     3000        self.dataWindow.SendSizeEvent()
     3001        # print 'SetDataSize'
     3002        # Size = self.GetSize()
     3003        # self.SetSize(Size)
     3004        # Size[1] += 1        #kluge to ensure scrollbar settings & window properly displayed
     3005        # self.SetSize(Size)
     3006                               
     3007    def OnDataTreeSelChanged(self, event):
     3008        '''Called when a data tree item is selected'''
     3009        if self.TreeItemDelete:
     3010            self.TreeItemDelete = False
     3011        else:
     3012            if self.ExpandingAll:
     3013                if GSASIIpath.GetConfigValue('debug'): print('Skipping Tree selection due to ExpandAll')
     3014                return
     3015            pltNum = self.G2plotNB.nb.GetSelection()
     3016            if pltNum >= 0:                         #to avoid the startup with no plot!
     3017                self.G2plotNB.nb.GetPage(pltNum)
     3018            item = event.GetItem()
     3019            wx.CallAfter(SelectDataTreeItem,self,item,self.oldFocus)
     3020            #if self.oldFocus: # now done via last parameter on SelectDataTreeItem
     3021            #    wx.CallAfter(self.oldFocus.SetFocus)
     3022       
     3023    def OnPatternTreeItemCollapsed(self, event):
     3024        'Called when a tree item is collapsed - all children will be collapsed'
     3025        self.PatternTree.CollapseAllChildren(event.GetItem())
     3026
     3027    def OnPatternTreeItemExpanded(self, event):
     3028        'Called when a tree item is expanded'
     3029        self.OnDataTreeSelChanged(event)
     3030        event.Skip()
     3031       
     3032    def OnPatternTreeItemDelete(self, event):
     3033        'Called when a tree item is deleted -- not sure what this does'
     3034        self.TreeItemDelete = True
     3035
     3036    def OnPatternTreeItemActivated(self, event):
     3037        'Called when a tree item is activated'
     3038        event.Skip()
     3039       
     3040    def OnPatternTreeBeginRDrag(self,event):
     3041        event.Allow()
     3042        self.BeginDragId = event.GetItem()
     3043        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
     3044        DragText = self.PatternTree.GetItemText(self.BeginDragId)
     3045        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
     3046        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
     3047        while item:     #G2 data tree has no sub children under a child of a tree item
     3048            name = self.PatternTree.GetItemText(item)
     3049            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
     3050            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
     3051       
     3052    def OnPatternTreeEndDrag(self,event):
     3053        event.Allow()
     3054        self.EndDragId = event.GetItem()
     3055        try:
     3056            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
     3057        except:
     3058            self.EndDragId = self.PatternTree.GetLastChild(self.root)
     3059            NewParent = self.root
     3060        if self.ParentId != NewParent:
     3061            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
     3062        else:
     3063            Name,Item = self.DragData[0]
     3064            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
     3065            self.PatternTree.SetItemPyData(NewId,Item)
     3066            for name,item in self.DragData[1:]:     #loop over children
     3067                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
     3068                self.PatternTree.SetItemPyData(Id,item)
     3069            self.PatternTree.Delete(self.BeginDragId)
     3070            SelectDataTreeItem(self,NewId)
     3071       
     3072    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
     3073        'Allows stepping through the tree with the up/down arrow keys'
     3074        self.oldFocus = wx.Window.FindFocus()
     3075        keyevt = event.GetKeyEvent()
     3076        key = event.GetKeyCode()
     3077        item = self.PatternTree.GetSelection()
     3078        if type(item) is int: return # is this the toplevel in tree?
     3079        name = self.PatternTree.GetItemText(item)
     3080        parent = self.PatternTree.GetItemParent(item)
     3081        if key == wx.WXK_UP:
     3082            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
     3083                if type(parent) is int: return # is this the toplevel in tree?
     3084                prev = self.PatternTree.GetPrevSibling(parent)
     3085                NewId = GetPatternTreeItemId(self,prev,name)
     3086                if NewId:
     3087                    self.PatternTree.Collapse(parent)
     3088                    self.PatternTree.Expand(prev)
     3089                    self.oldFocus = wx.Window.FindFocus()
     3090                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
     3091                else:
     3092                    wx.CallAfter(self.PatternTree.SelectItem,item)
     3093            elif sys.platform == "win32":   
     3094                self.PatternTree.GetPrevSibling(item)
     3095                self.PatternTree.SelectItem(item)
     3096            else:
     3097                item = self.PatternTree.GetPrevSibling(item)
     3098                if item.IsOk(): self.PatternTree.SelectItem(item)
     3099        elif key == wx.WXK_DOWN:
     3100            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
     3101                next = self.PatternTree.GetNextSibling(parent)
     3102                NewId = GetPatternTreeItemId(self,next,name)
     3103                if NewId:
     3104                    self.PatternTree.Collapse(parent)
     3105                    self.PatternTree.Expand(next)
     3106                    self.oldFocus = wx.Window.FindFocus()
     3107                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
     3108                else:
     3109                    wx.CallAfter(self.PatternTree.SelectItem,item)
     3110            elif sys.platform == "win32":   
     3111                self.PatternTree.GetNextSibling(item)
     3112                self.PatternTree.SelectItem(item)
     3113            else:   
     3114                item = self.PatternTree.GetNextSibling(item)
     3115                if item.IsOk(): self.PatternTree.SelectItem(item)
     3116               
     3117    def OnReadPowderPeaks(self,event):
     3118        'Bound to menu Data/Read Powder Peaks'
     3119        self.CheckNotebook()
     3120        pth = G2G.GetImportPath(self)
     3121        if not pth: pth = '.'
     3122        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '',
     3123            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
     3124        try:
     3125            if dlg.ShowModal() == wx.ID_OK:
     3126                self.HKL = []
     3127                self.powderfile = dlg.GetPath()
     3128                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
     3129                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
     3130                data = ['PKS',wave,0.0]
     3131                names = ['Type','Lam','Zero']
     3132                codes = [0,0,0]
     3133                inst = [G2IO.makeInstDict(names,data,codes),{}]
     3134                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
     3135                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
     3136                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
     3137                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
     3138                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
     3139                self.PatternTree.Expand(Id)
     3140                self.PatternTree.SelectItem(Id)
     3141                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
     3142        finally:
     3143            dlg.Destroy()
     3144                       
     3145    # def OnImageRead(self,event):
     3146    #     '''Called to read in an image in any known format. *** Depreciated. ***
     3147    #     '''
     3148    #     G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
     3149
     3150    def CheckNotebook(self):
     3151        '''Make sure the data tree has the minimally expected controls.
     3152        '''
     3153        if not GetPatternTreeItemId(self,self.root,'Notebook'):
     3154            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
     3155            self.PatternTree.SetItemPyData(sub,[''])
     3156        if not GetPatternTreeItemId(self,self.root,'Controls'):
     3157            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
     3158            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
     3159        if not GetPatternTreeItemId(self,self.root,'Covariance'):
     3160            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
     3161            self.PatternTree.SetItemPyData(sub,{})
     3162        if not GetPatternTreeItemId(self,self.root,'Constraints'):
     3163            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
     3164            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
     3165        if not GetPatternTreeItemId(self,self.root,'Restraints'):
     3166            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
     3167            self.PatternTree.SetItemPyData(sub,{})
     3168        if not GetPatternTreeItemId(self,self.root,'Rigid bodies'):
     3169            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
     3170            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
     3171                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
     3172               
     3173    class CopyDialog(wx.Dialog):
     3174        '''Creates a dialog for copying control settings between
     3175        data tree items'''
     3176        def __init__(self,parent,title,text,data):
     3177            wx.Dialog.__init__(self,parent,-1,title,
     3178                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
     3179            self.data = data
     3180            panel = wx.Panel(self)
     3181            mainSizer = wx.BoxSizer(wx.VERTICAL)
     3182            topLabl = wx.StaticText(panel,-1,text)
     3183            mainSizer.Add((10,10),1)
     3184            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
     3185            mainSizer.Add((10,10),1)
     3186            ncols = len(data)/40+1
     3187            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
     3188            for id,item in enumerate(self.data):
     3189                ckbox = wx.CheckBox(panel,id,item[1])
     3190                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
     3191                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
     3192            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
     3193            OkBtn = wx.Button(panel,-1,"Ok")
     3194            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
     3195            cancelBtn = wx.Button(panel,-1,"Cancel")
     3196            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
     3197            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
     3198            btnSizer.Add((20,20),1)
     3199            btnSizer.Add(OkBtn)
     3200            btnSizer.Add((20,20),1)
     3201            btnSizer.Add(cancelBtn)
     3202            btnSizer.Add((20,20),1)
     3203           
     3204            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
     3205            panel.SetSizer(mainSizer)
     3206            panel.Fit()
     3207            self.Fit()
     3208       
     3209        def OnCopyChange(self,event):
     3210            id = event.GetId()
     3211            self.data[id][0] = self.FindWindowById(id).GetValue()       
     3212           
     3213        def OnOk(self,event):
     3214            parent = self.GetParent()
     3215            parent.Raise()
     3216            self.EndModal(wx.ID_OK)             
     3217           
     3218        def OnCancel(self,event):
     3219            parent = self.GetParent()
     3220            parent.Raise()
     3221            self.EndModal(wx.ID_CANCEL)             
     3222           
     3223        def GetData(self):
     3224            return self.data
     3225       
     3226    class SumDialog(wx.Dialog):
     3227        '''Allows user to supply scale factor(s) when summing data -
     3228        TODO: CAN WE PREVIEW RESULT HERE?'''
     3229        def __init__(self,parent,title,text,dataType,data,dataList):
     3230            wx.Dialog.__init__(self,parent,-1,title,size=(400,250),
     3231                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
     3232            self.plotFrame = wx.Frame(self,-1,'Sum Plots',size=wx.Size(700,600), \
     3233                style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
     3234            self.G2plotNB = G2plt.G2PlotNoteBook(self.plotFrame,G2frame=self)
     3235            self.data = data
     3236            self.dataList = dataList
     3237            self.dataType = dataType
     3238            size = (450,350)
     3239            panel = wxscroll.ScrolledPanel(self, wx.ID_ANY,size=size,
     3240                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
     3241            mainSizer = wx.BoxSizer(wx.VERTICAL)
     3242            topLabl = wx.StaticText(panel,-1,text)
     3243            mainSizer.Add((10,10),1)
     3244            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
     3245            mainSizer.Add((10,10),1)
     3246            self.dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
     3247            for id,item in enumerate(self.data[:-1]):
     3248                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(300,20))
     3249                name.SetEditable(False)
     3250#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
     3251                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
     3252                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
     3253                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
     3254                self.dataGridSizer.Add(scale,0,wx.LEFT,10)
     3255                self.dataGridSizer.Add(name,0,wx.RIGHT,10)
     3256            if self.dataType:
     3257                self.dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+self.dataType),0, \
     3258                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
     3259                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(300,20),style=wx.TE_PROCESS_ENTER)
     3260                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
     3261                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
     3262                self.dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
     3263                self.dataGridSizer.Add(wx.StaticText(panel,label='All scales value: '),0,  \
     3264                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
     3265#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
     3266                allScale = wx.TextCtrl(panel,value='',style=wx.TE_PROCESS_ENTER)
     3267                allScale.Bind(wx.EVT_TEXT_ENTER,self.OnAllScale)
     3268                allScale.Bind(wx.EVT_KILL_FOCUS,self.OnAllScale)
     3269                self.dataGridSizer.Add(allScale,0,WACV)
     3270            mainSizer.Add(self.dataGridSizer,0,wx.EXPAND)
     3271            OkBtn = wx.Button(panel,-1,"Ok")
     3272            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
     3273            cancelBtn = wx.Button(panel,-1,"Cancel")
     3274            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
     3275            btnSizer = wx.FlexGridSizer(0,3,10,20)
     3276            if self.dataType =='PWDR':
     3277                TestBtn = wx.Button(panel,-1,"Test")
     3278                TestBtn.Bind(wx.EVT_BUTTON, self.OnTest)
     3279                btnSizer.Add(TestBtn)
     3280            btnSizer.Add(OkBtn)
     3281            btnSizer.Add(cancelBtn)
     3282           
     3283            panel.SetSizer(mainSizer)
     3284            panel.SetAutoLayout(1)
     3285            panel.SetupScrolling()
     3286            mainSizer.Add((10,10),1)
     3287            mainSizer.Add(btnSizer,0,wx.CENTER)
     3288            panel.SetSizer(mainSizer)
     3289            panel.Fit()
     3290            self.Fit()
     3291
     3292        def OnScaleChange(self,event):
     3293            event.Skip()
     3294            id = event.GetId()
     3295            value = self.FindWindowById(id).GetValue()
     3296            try:
     3297                self.data[id][0] = float(value)
     3298                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
     3299            except ValueError:
     3300                if value and '-' not in value[0]:
     3301                    print 'bad input - numbers only'
     3302                    self.FindWindowById(id).SetValue('0.000')
     3303                   
     3304        def OnAllScale(self,event):
     3305            event.Skip()
     3306            id = event.GetId()
     3307            try:
     3308                scale = float(self.FindWindowById(id).GetValue())
     3309                self.FindWindowById(id).SetValue('%.3f'%(scale))
     3310                entries = self.dataGridSizer.GetChildren()
     3311                for i,item in enumerate(self.data[:-1]):
     3312                    item[0] = scale
     3313                    entries[2*i].GetWindow().SetValue('%.3f'%(scale))
     3314                 
     3315            except ValueError:
     3316                print 'bad input - numbers only'
     3317                self.FindWindowById(id).SetValue('')
     3318                   
     3319           
     3320        def OnNameChange(self,event):
     3321            event.Skip()
     3322            self.data[-1] = self.name.GetValue()
     3323           
     3324        def OnTest(self,event):
     3325            lenX = 0
     3326            Xminmax = [0,0]
     3327            XY = []
     3328            Xsum = []
     3329            Ysum = []
     3330            Vsum = []
     3331            result = self.data
     3332            for i,item in enumerate(result[:-1]):
     3333                scale,name = item
     3334                data = self.dataList[i]
     3335                if scale:
     3336                    x,y,w,yc,yb,yd = data   #numpy arrays!
     3337                    XY.append([x,scale*y])
     3338                    v = 1./w
     3339                    if lenX:
     3340                        if lenX != len(x):
     3341                            self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
     3342                                '\nExpected:'+str(lenX)+ \
     3343                                '\nFound:   '+str(len(x))+'\nfor '+name)
     3344                            self.OnCancel(event)
     3345                    else:
     3346                        lenX = len(x)
     3347                    if Xminmax[1]:
     3348                        if Xminmax != [x[0],x[-1]]:
     3349                            self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
     3350                                '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
     3351                                '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
     3352                            self.OnCancel(event)
     3353                        else:
     3354                            for j,yi in enumerate(y):
     3355                                 Ysum[j] += scale*yi
     3356                                 Vsum[j] += abs(scale)*v[j]
     3357                    else:
     3358                        Xminmax = [x[0],x[-1]]
     3359                        Xsum = x
     3360                        Ysum = scale*y
     3361                        Vsum = abs(scale*v)
     3362            Wsum = 1./np.array(Vsum)
     3363            YCsum = np.zeros(lenX)
     3364            YBsum = np.zeros(lenX)
     3365            YDsum = np.zeros(lenX)
     3366            XY.append([Xsum,Ysum])
     3367            self.result = [Xsum,Ysum,Wsum,YCsum,YBsum,YDsum]
     3368            # N.B. PlotXY expects the first arg to point to G2frame. In this case, we
     3369            # create a duplicate (temporary) Plot notebook window that is a child of the
     3370            # modal SumDialog dialog (self). This nicely gets deleted when the dialog is destroyed,
     3371            # but the plot window is not fully functional, at least on the Mac.
     3372            G2plt.PlotXY(self,XY,lines=True,Title='Sum:'+self.data[-1],labelY='Intensity',)
     3373            self.plotFrame.Show()
     3374                       
     3375        def OnOk(self,event):
     3376            if self.dataType == 'PWDR': self.OnTest(event)
     3377            parent = self.GetParent()
     3378            parent.Raise()
     3379            self.EndModal(wx.ID_OK)             
     3380           
     3381        def OnCancel(self,event):
     3382            parent = self.GetParent()
     3383            parent.Raise()
     3384            self.EndModal(wx.ID_CANCEL)             
     3385           
     3386        def GetData(self):
     3387            if self.dataType == 'PWDR':
     3388                return self.data,self.result
     3389            else:
     3390                return self.data
     3391                       
     3392    def OnPwdrSum(self,event):
     3393        'Sum together powder data(?)'
     3394        TextList = []
     3395        DataList = []
     3396        Names = []
     3397        Inst = None
     3398        Comments = ['Sum equals: \n']
     3399        if self.PatternTree.GetCount():
     3400            item, cookie = self.PatternTree.GetFirstChild(self.root)
     3401            while item:
     3402                name = self.PatternTree.GetItemText(item)
     3403                Names.append(name)
     3404                if 'PWDR' in name:
     3405                    TextList.append([0.0,name])
     3406                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
     3407                    if not Inst:
     3408                        Inst = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,item, 'Instrument Parameters'))
     3409                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     3410            if len(TextList) < 2:
     3411                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
     3412                return
     3413            TextList.append('default_sum_name')               
     3414            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList,DataList)
     3415            try:
     3416                if dlg.ShowModal() == wx.ID_OK:
     3417                    result,sumData = dlg.GetData()
     3418                    Xsum,Ysum,Wsum,YCsum,YBsum,YDsum = sumData
     3419                    Xminmax = [Xsum[0],Xsum[-1]]
     3420                    outname = 'PWDR '+result[-1]
     3421                    Id = 0
     3422                    if outname in Names:
     3423                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
     3424                        try:
     3425                            if dlg2.ShowModal() == wx.ID_OK:
     3426                                Id = GetPatternTreeItemId(self,self.root,name)
     3427                                self.PatternTree.Delete(Id)
     3428                        finally:
     3429                            dlg2.Destroy()
     3430                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
     3431                    if Id:
     3432                        Sample = G2obj.SetDefaultSample()
     3433                        Ymin = np.min(Ysum)
     3434                        Ymax = np.max(Ysum)
     3435                        valuesdict = {
     3436                            'wtFactor':1.0,
     3437                            'Dummy':False,
     3438                            'ranId':ran.randint(0,sys.maxint),
     3439                            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
     3440                            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
     3441                            }
     3442                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
     3443                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
     3444                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
     3445                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
     3446                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
     3447                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
     3448                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
     3449                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
     3450                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
     3451                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
     3452                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
     3453                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
     3454                        self.PatternTree.SelectItem(Id)
     3455                        self.PatternTree.Expand(Id)
     3456            finally:
     3457                dlg.Destroy()
     3458
     3459    def OnImageSum(self,event):
     3460        'Sum together image data'
     3461        TextList = []
     3462        DataList = []
     3463        IdList = []
     3464        Names = []
     3465        Comments = ['Sum equals: \n']
     3466        if self.PatternTree.GetCount():
     3467            item, cookie = self.PatternTree.GetFirstChild(self.root)
     3468            while item:
     3469                name = self.PatternTree.GetItemText(item)
     3470                Names.append(name)
     3471                if 'IMG' in name:
     3472                    TextList.append([0.0,name])
     3473                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
     3474                    IdList.append(item)
     3475                    Data = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,item,'Image Controls'))
     3476                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     3477            if len(TextList) < 2:
     3478                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
     3479                return
     3480            TextList.append('default_sum_name')               
     3481            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList,DataList)
     3482            try:
     3483                if dlg.ShowModal() == wx.ID_OK:
     3484                    imSize = 0
     3485                    result = dlg.GetData()
     3486                    First = True
     3487                    Found = False
     3488                    for i,item in enumerate(result[:-1]):
     3489                        scale,name = item
     3490                        if scale:
     3491                            Found = True                               
     3492                            Comments.append("%10.3f %s" % (scale,' * '+name))
     3493                            Npix,imagefile,imagetag = DataList[i]
     3494                            imagefile = G2IO.GetCheckImageFile(self,IdList[i])[1]
     3495                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
     3496                            if First:
     3497                                newImage = np.zeros_like(image)
     3498                                First = False
     3499                            if imSize:
     3500                                if imSize != Npix:
     3501                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
     3502                                        '\nExpected:'+str(imSize)+ \
     3503                                        '\nFound:   '+str(Npix)+'\nfor '+name)
     3504                                    return
     3505                                newImage = newImage+scale*image
     3506                            else:
     3507                                imSize = Npix
     3508                                newImage = newImage+scale*image
     3509                            del(image)
     3510                    if not Found:
     3511                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
     3512                        return
     3513                       
     3514                       
     3515                    newImage = np.array(newImage,dtype=np.int32)                       
     3516                    outname = 'IMG '+result[-1]
     3517                    Id = 0
     3518                    if outname in Names:
     3519                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
     3520                        try:
     3521                            if dlg2.ShowModal() == wx.ID_OK:
     3522                                Id = GetPatternTreeItemId(self,self.root,name)
     3523                        finally:
     3524                            dlg2.Destroy()
     3525                    else:
     3526                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
     3527                    if Id:
     3528                        pth = G2G.GetExportPath(self)
     3529                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth,outname.split('IMG ')[1],
     3530                            'G2img files (*.G2img)|*.G2img',
     3531                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
     3532                        if dlg.ShowModal() == wx.ID_OK:
     3533                            newimagefile = dlg.GetPath()
     3534                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
     3535                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
     3536                            Imax = np.amax(newImage)
     3537                            Imin = np.amin(newImage)
     3538                            newImage = []
     3539                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
     3540                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
     3541                        del(newImage)
     3542                        if self.imageDefault:
     3543                            Data = copy.copy(self.imageDefault)
     3544                        Data['formatName'] = 'GSAS-II image'
     3545                        Data['showLines'] = True
     3546                        Data['ring'] = []
     3547                        Data['rings'] = []
     3548                        Data['cutoff'] = 10
     3549                        Data['pixLimit'] = 20
     3550                        Data['ellipses'] = []
     3551                        Data['calibrant'] = ''
     3552                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
     3553                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
     3554                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
     3555                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
     3556                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
     3557                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
     3558                        self.PatternTree.SelectItem(Id)
     3559                        self.PatternTree.Expand(Id)
     3560                        self.PickId = GetPatternTreeItemId(self,self.root,outname)
     3561                        self.Image = self.PickId
     3562            finally:
     3563                dlg.Destroy()
     3564                     
     3565    def OnAddPhase(self,event):
     3566        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
     3567        if not GetPatternTreeItemId(self,self.root,'Phases'):
     3568            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
     3569        else:
     3570            sub = GetPatternTreeItemId(self,self.root,'Phases')
     3571        PhaseName = ''
     3572        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
     3573            style=wx.OK)
     3574        if dlg.ShowModal() == wx.ID_OK:
     3575            PhaseName = dlg.GetValue()
     3576        dlg.Destroy()
     3577        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
     3578        E,SGData = G2spc.SpcGroup('P 1')
     3579        self.PatternTree.SetItemPyData(sub,G2obj.SetNewPhase(Name=PhaseName,SGData=SGData))
     3580        SelectDataTreeItem(self,sub) #bring up new phase General tab
     3581       
     3582    def OnDeletePhase(self,event):
     3583        'Delete a phase from the tree. Called by Data/Delete Phase menu'
     3584        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
     3585        if self.dataFrame:
     3586            self.dataFrame.Clear()
     3587        TextList = []
     3588        DelList = []
     3589        DelItemList = []
     3590        if GetPatternTreeItemId(self,self.root,'Phases'):
     3591            sub = GetPatternTreeItemId(self,self.root,'Phases')
     3592        else:
     3593            return
     3594        if sub:
     3595            item, cookie = self.PatternTree.GetFirstChild(sub)
     3596            while item:
     3597                TextList.append(self.PatternTree.GetItemText(item))
     3598                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
     3599            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
     3600            try:
     3601                if dlg.ShowModal() == wx.ID_OK:
     3602                    result = dlg.GetSelections()
     3603                    for i in result: DelList.append([i,TextList[i]])
     3604                    item, cookie = self.PatternTree.GetFirstChild(sub)
     3605                    i = 0
     3606                    while item:
     3607                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
     3608                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     3609                        i += 1
     3610                    for item in DelItemList:
     3611                        name = self.PatternTree.GetItemText(item)
     3612                        self.PatternTree.Delete(item)
     3613                        self.G2plotNB.Delete(name)
     3614                    item, cookie = self.PatternTree.GetFirstChild(self.root)
     3615                    while item:
     3616                        name = self.PatternTree.GetItemText(item)
     3617                        if 'PWDR' in name:
     3618                            Id = GetPatternTreeItemId(self,item, 'Reflection Lists')
     3619                            refList = self.PatternTree.GetItemPyData(Id)
     3620                            if len(refList):
     3621                                for i,item in DelList:
     3622                                    if item in refList:
     3623                                        del(refList[item])
     3624#                            self.PatternTree.SetItemPyData(Id,refList)
     3625                        elif 'HKLF' in name:
     3626                            data = self.PatternTree.GetItemPyData(item)
     3627                            data[0] = {}
     3628#                            self.PatternTree.SetItemPyData(item,data)
     3629                           
     3630                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     3631            finally:
     3632                dlg.Destroy()
     3633               
     3634    def OnRenameData(self,event):
     3635        'Renames an existing phase. Called by Data/Rename Phase menu'
     3636        name = self.PatternTree.GetItemText(self.PickId)     
     3637        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
     3638            if 'Bank' in name:
     3639                names = name.split('Bank')
     3640                names[1] = ' Bank'+names[1]
     3641            elif 'Azm' in name:
     3642                names = name.split('Azm')
     3643                names[1] = ' Azm'+names[1]
     3644            else:
     3645                names = [name,'']
     3646            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
     3647            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
     3648                defaultValue=names[0][names[0].index(' ')+1:])
     3649            try:
     3650                if dlg.ShowModal() == wx.ID_OK:
     3651                    name = dataType+dlg.GetValue()+names[1]
     3652                    self.PatternTree.SetItemText(self.PickId,name)
     3653            finally:
     3654                dlg.Destroy()
     3655       
     3656    def GetFileList(self,fileType,skip=None):        #potentially useful?
     3657        'Appears unused. Note routine of same name in GSASIIpwdGUI'
     3658        fileList = []
     3659        Source = ''
     3660        id, cookie = self.PatternTree.GetFirstChild(self.root)
     3661        while id:
     3662            name = self.PatternTree.GetItemText(id)
     3663            if fileType in name:
     3664                if id == skip:
     3665                    Source = name
     3666                else:
     3667                    fileList.append([False,name,id])
     3668            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     3669        if skip:
     3670            return fileList,Source
     3671        else:
     3672            return fileList
     3673           
     3674    def OnDataDelete(self, event):
     3675        '''Delete one or more histograms from data tree. Called by the
     3676        Data/DeleteData menu
     3677        '''
     3678        TextList = []
     3679        DelList = []
     3680        DelItemList = []
     3681        nItems = {'PWDR':0,'SASD':0,'REFD':0,'IMG':0,'HKLF':0,'PDF':0}
     3682        PDFnames = []
     3683        if self.PatternTree.GetCount():
     3684            item, cookie = self.PatternTree.GetFirstChild(self.root)
     3685            while item:
     3686                name = self.PatternTree.GetItemText(item)
     3687                if name not in ['Notebook','Controls','Covariance','Constraints',
     3688                    'Restraints','Phases','Rigid bodies'] and 'Sequential' not in name:
     3689                    if 'PWDR' in name[:4]: nItems['PWDR'] += 1
     3690                    if 'SASD' in name[:4]: nItems['SASD'] += 1
     3691                    if 'REFD' in name[:4]: nItems['REFD'] += 1
     3692                    if 'IMG' in name[:3]:  nItems['IMG'] += 1
     3693                    if 'HKLF' in name[:4]: nItems['HKLF'] += 1
     3694                    if 'PDF' in name[:3]:
     3695                        PDFnames.append(name)
     3696                        nItems['PDF'] += 1
     3697                    TextList.append(name)
     3698                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     3699            for pdfName in PDFnames:
     3700                try:
     3701                    TextList.remove('PWDR'+pdfName[4:])
     3702                except ValueError:
     3703                    print 'PWDR'+pdfName[4:]+' for '+pdfName+' not found'
     3704            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
     3705            try:
     3706                if dlg.ShowModal() == wx.ID_OK:
     3707                    result = dlg.GetSelections()
     3708                    for i in result: DelList.append(TextList[i])
     3709                    item, cookie = self.PatternTree.GetFirstChild(self.root)
     3710                    while item:
     3711                        itemName = self.PatternTree.GetItemText(item)
     3712                        if itemName in DelList:
     3713                            if 'PWDR' in itemName[:4]: nItems['PWDR'] -= 1
     3714                            elif 'SASD' in itemName[:4]: nItems['SASD'] -= 1
     3715                            elif 'REFD' in itemName[:4]: nItems['REFD'] -= 1
     3716                            elif 'IMG' in itemName[:3]: nItems['IMG'] -= 1
     3717                            elif 'HKLF' in itemName[:4]: nItems['HKLF'] -= 1
     3718                            elif 'PDF' in itemName[:3]: nItems['PDF'] -= 1
     3719                            DelItemList.append(item)
     3720                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     3721                    for item in DelItemList:
     3722                        self.PatternTree.Delete(item)
     3723                    self.PickId = 0
     3724                    self.PickIdText = None
     3725                    self.PatternId = 0
     3726                    if nItems['PWDR']:
     3727                        wx.CallAfter(G2plt.PlotPatterns,self,True)
     3728                    else:
     3729                        self.G2plotNB.Delete('Powder Patterns')
     3730                    if not nItems['IMG']:
     3731                        self.G2plotNB.Delete('2D Powder Image')
     3732                    if not nItems['HKLF']:
     3733                        self.G2plotNB.Delete('Structure Factors')
     3734                        if '3D Structure Factors' in self.G2plotNB.plotList:
     3735                            self.G2plotNB.Delete('3D Structure Factors')
     3736            finally:
     3737                dlg.Destroy()
     3738
     3739    def OnFileOpen(self, event, filename=None):
     3740        '''Gets a GSAS-II .gpx project file in response to the
     3741        File/Open Project menu button
     3742        '''
     3743        result = wx.ID_OK
     3744        self.EnablePlot = False
     3745        if self.PatternTree.GetChildrenCount(self.root,False):
     3746            if self.dataFrame:
     3747                self.dataFrame.ClearData()
     3748            dlg = wx.MessageDialog(
     3749                self,
     3750                'Do you want to overwrite the current project? '+
     3751                'Any unsaved changes in current project will be lost. Press OK to continue.',
     3752                'Overwrite?',  wx.OK | wx.CANCEL)
     3753            try:
     3754                result = dlg.ShowModal()
     3755                if result == wx.ID_OK:
     3756                    self.PatternTree.DeleteChildren(self.root)
     3757                    self.GSASprojectfile = ''
     3758                    self.HKL = []
     3759                    if self.G2plotNB.plotList:
     3760                        self.G2plotNB.clear()
     3761            finally:
     3762                dlg.Destroy()
     3763        if result != wx.ID_OK: return
     3764
     3765        if not filename:
     3766            if self.LastGPXdir:
     3767                pth = self.LastGPXdir
     3768            else:
     3769                pth = '.'
     3770            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth,
     3771                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
     3772            try:
     3773                if dlg.ShowModal() != wx.ID_OK: return
     3774                self.GSASprojectfile = dlg.GetPath()
     3775                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
     3776                self.dirname = dlg.GetDirectory()
     3777            finally:
     3778                dlg.Destroy()
     3779        else:
     3780            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
     3781            self.dirname = os.path.split(filename)[0]
     3782
     3783        try:
     3784            self.StartProject()         #open the file if possible
     3785        except:
     3786            print '\nError opening file ',filename
     3787            import traceback
     3788            print traceback.format_exc()
     3789       
     3790    def StartProject(self):
     3791        '''Opens a GSAS-II project file & selects the 1st available data set to
     3792        display (PWDR, HKLF, REFD or SASD)
     3793        '''
     3794       
     3795        Id = 0
     3796        phaseId = None
     3797        G2IO.ProjFileOpen(self)
     3798        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
     3799        self.PatternTree.Expand(self.root)
     3800        self.HKL = []
     3801        item, cookie = self.PatternTree.GetFirstChild(self.root)
     3802        while item and not Id:
     3803            name = self.PatternTree.GetItemText(item)
     3804            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD','REFD']:
     3805                Id = item
     3806            elif name == "Phases":
     3807                phaseId = item
     3808            elif name == 'Controls':
     3809                data = self.PatternTree.GetItemPyData(item)
     3810                if data:
     3811                    for item in self.Refine: item.Enable(True)
     3812                    self.EnableSeqRefineMenu()
     3813            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     3814        if phaseId: # show all phases
     3815            self.PatternTree.Expand(phaseId)
     3816        if Id:
     3817            self.EnablePlot = True
     3818            self.PatternTree.SelectItem(Id)
     3819            self.PatternTree.Expand(Id)
     3820        elif phaseId:
     3821            self.PatternTree.SelectItem(phaseId)
     3822        self.CheckNotebook()
     3823        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
     3824        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
     3825        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
     3826        self.LastGPXdir = pth
     3827
     3828    def OnFileClose(self, event):
     3829        '''Clears the data tree in response to the
     3830        File/New Project menu button. User is given option to save
     3831        the project.
     3832        '''
     3833#        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
     3834        try:
     3835            result = dlg.ShowModal()
     3836            if result == wx.ID_OK:
     3837                self.OnFileSaveMenu(event)
     3838            if result != wx.ID_CANCEL:
     3839                self.GSASprojectfile = ''
     3840                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
     3841                self.PatternTree.DeleteChildren(self.root)
     3842                if self.HKL: self.HKL = []
     3843                if self.G2plotNB.plotList:
     3844                    self.G2plotNB.clear()
     3845        finally:
     3846            dlg.Destroy()
     3847
     3848    def OnFileSave(self, event):
     3849        '''Save the current project in response to the
     3850        File/Save Project menu button
     3851        '''
     3852       
     3853        if self.GSASprojectfile:
     3854            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
     3855            self.CheckNotebook()
     3856            G2IO.ProjFileSave(self)
     3857        else:
     3858            self.OnFileSaveas(event)
     3859
     3860    def OnFileSaveas(self, event):
     3861        '''Save the current project in response to the
     3862        File/Save as menu button
     3863        '''
     3864        if GSASIIpath.GetConfigValue('Starting_directory'):
     3865            pth = GSASIIpath.GetConfigValue('Starting_directory')
     3866            pth = os.path.expanduser(pth)
     3867        elif self.LastGPXdir:
     3868            pth = self.LastGPXdir
     3869        else:
     3870            pth = '.'
     3871        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '',
     3872            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
     3873        try:
     3874            if dlg.ShowModal() == wx.ID_OK:
     3875                self.GSASprojectfile = dlg.GetPath()
     3876                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
     3877                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
     3878                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
     3879                self.CheckNotebook()
     3880                G2IO.ProjFileSave(self)
     3881                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
     3882        finally:
     3883            dlg.Destroy()
     3884           
     3885    def ExpandAll(self,event):
     3886        '''Expand all tree items or those of a single type
     3887        '''
     3888        txt = self.GetMenuBar().GetLabel(event.Id)
     3889        if txt == 'all':
     3890            self.ExpandingAll = True
     3891            try:
     3892                self.PatternTree.ExpandAll()
     3893            finally:
     3894                self.ExpandingAll = False
     3895        else:
     3896            self.ExpandingAll = True
     3897            try:
     3898                item, cookie = self.PatternTree.GetFirstChild(self.root)
     3899                while item:
     3900                    name = self.PatternTree.GetItemText(item)
     3901                    if name.startswith(txt+' '): self.PatternTree.Expand(item)
     3902                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     3903            finally:
     3904                self.ExpandingAll = False
     3905
     3906    def MoveTreeItems(self,event):
     3907        '''Move tree items of a single type to the end of the tree
     3908        '''
     3909        txt = self.GetMenuBar().GetLabel(event.Id)
     3910        # make a list of items to copy
     3911        copyList = []
     3912        item, cookie = self.PatternTree.GetFirstChild(self.root)
     3913        while item:
     3914            if self.PatternTree.GetItemText(item).startswith(txt+' '):
     3915                copyList.append(item)
     3916            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     3917       
     3918        self.ExpandingAll = True
     3919        try:
     3920            for item in copyList:
     3921                name = self.PatternTree.GetItemText(item)
     3922                newId = self.PatternTree.AppendItem(self.root,name)
     3923                self.PatternTree.SetItemPyData(newId,self.PatternTree.GetItemPyData(item))
     3924                chld, chldcookie = self.PatternTree.GetFirstChild(item)
     3925                while chld:
     3926                    chname = self.PatternTree.GetItemText(chld)
     3927                    newCh = self.PatternTree.AppendItem(newId,chname)
     3928                    self.PatternTree.SetItemPyData(newCh,self.PatternTree.GetItemPyData(chld))
     3929                    chld, chldcookie = self.PatternTree.GetNextChild(item, chldcookie)
     3930                self.PatternTree.Delete(item)
     3931        finally:
     3932            self.ExpandingAll = False
     3933        SelectDataTreeItem(self,self.root)
     3934           
     3935    def ExitMain(self, event):
     3936        '''Called if the main window is closed'''
     3937        if self.G2plotNB:
     3938            self.G2plotNB.Destroy()
     3939        if self.dataFrame:
     3940            self.dataFrame.Destroy()
     3941        if self.undofile:
     3942            os.remove(self.undofile)
     3943        sys.exit()
     3944       
     3945    def OnFileExit(self, event):
     3946        '''Called in response to the File/Quit menu button'''
     3947        if self.G2plotNB:
     3948            self.G2plotNB.Destroy()
     3949        if self.dataFrame:
     3950            self.dataFrame.Destroy()
     3951        self.Close()
     3952       
     3953    def OnExportPeakList(self,event):
     3954        nptand = lambda x: np.tan(x*math.pi/180.)
     3955        pth = G2G.GetExportPath(self)
     3956        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '',
     3957            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
     3958        try:
     3959            if dlg.ShowModal() == wx.ID_OK:
     3960                self.peaklistfile = dlg.GetPath()
     3961                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
     3962                file = open(self.peaklistfile,'w')               
     3963                item, cookie = self.PatternTree.GetFirstChild(self.root)
     3964                while item:
     3965                    name = self.PatternTree.GetItemText(item)
     3966                    if 'PWDR' in name:
     3967                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
     3968                        wave = 0.0
     3969                        while item2:
     3970                            name2 = self.PatternTree.GetItemText(item2)
     3971                            if name2 == 'Instrument Parameters':
     3972                                Inst = self.PatternTree.GetItemPyData(item2)[0]
     3973                                Type = Inst['Type'][0]
     3974                                if 'T' not in Type:
     3975                                    wave = G2mth.getWave(Inst)
     3976                            elif name2 == 'Peak List':
     3977                                pkdata = self.PatternTree.GetItemPyData(item2)
     3978                                peaks = pkdata['peaks']
     3979                                sigDict = pkdata['sigDict']
     3980                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
     3981                        file.write("#%s \n" % (name+' Peak List'))
     3982                        if wave:
     3983                            file.write('#wavelength = %10.6f\n'%(wave))
     3984                        if 'T' in Type:
     3985                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
     3986                        else:
     3987                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
     3988                        for ip,peak in enumerate(peaks):
     3989                            dsp = G2lat.Pos2dsp(Inst,peak[0])
     3990                            if 'T' in Type:  #TOF - more cols
     3991                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
     3992                                for name in esds.keys():
     3993                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
     3994                                sig = np.sqrt(peak[8])
     3995                                gam = peak[10]
     3996                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
     3997                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
     3998                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
     3999                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
     4000                            else:               #CW
     4001                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
     4002                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
     4003                                for name in esds.keys():
     4004                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
     4005                                sig = np.sqrt(peak[4]) #var -> sig
     4006                                gam = peak[6]
     4007                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
     4008                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
     4009                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
     4010                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
     4011                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
     4012                file.close()
     4013        finally:
     4014            dlg.Destroy()
     4015       
     4016    def OnExportHKL(self,event):
     4017        pth = G2G.GetExportPath(self)
     4018        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '',
     4019            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
     4020        try:
     4021            if dlg.ShowModal() == wx.ID_OK:
     4022                self.peaklistfile = dlg.GetPath()
     4023                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
     4024                file = open(self.peaklistfile,'w')               
     4025                item, cookie = self.PatternTree.GetFirstChild(self.root)
     4026                while item:
     4027                    name = self.PatternTree.GetItemText(item)
     4028                    if 'PWDR' in name:
     4029                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
     4030                        while item2:
     4031                            name2 = self.PatternTree.GetItemText(item2)
     4032                            if name2 == 'Reflection Lists':
     4033                                data = self.PatternTree.GetItemPyData(item2)
     4034                                phases = data.keys()
     4035                                for phase in phases:
     4036                                    peaks = data[phase]
     4037                                    I100 = peaks['RefList'].T[8]*np.array([refl[11] for refl in peaks['RefList']])
     4038                                    Imax = np.max(I100)
     4039                                    if Imax:
     4040                                        I100 *= 100.0/Imax
     4041                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
     4042                                    if 'T' in peaks.get('Type','PXC'):
     4043                                        file.write('%s \n'%('   h   k   l   m   d-space       TOF       wid     Fo**2     Fc**2     Icorr      Prfo     Trans      ExtP      I100'))
     4044                                    else:               
     4045                                        file.write('%s \n'%('   h   k   l   m   d-space   2-theta       wid     Fo**2     Fc**2     Icorr      Prfo     Trans      ExtP      I100'))
     4046                                    for ipk,peak in enumerate(peaks['RefList']):
     4047                                        if 'T' in peaks.get('Type','PXC'):
     4048                                            sig = np.sqrt(peak[6])
     4049                                            gam = peak[7]
     4050                                            FWHM = G2pwd.getgamFW(gam,sig)
     4051                                            file.write(" %3d %3d %3d %3d%10.5f%10.2f%10.5f%10.3f%10.3f%10.3f%10.3f%10.3f%10.3f%10.3f\n" % \
     4052                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8],
     4053                                                peak[9],peak[11],peak[12],peak[13],peak[14],I100[ipk]))
     4054                                        else:
     4055                                            sig = np.sqrt(peak[6])
     4056                                            gam = peak[7]
     4057                                            FWHM = G2pwd.getgamFW(gam,sig)
     4058                                            file.write(" %3d %3d %3d %3d%10.5f%10.5f%10.5f%10.3f%10.3f%10.3f%10.3f%10.3f%10.3f%10.3f\n" % \
     4059                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,
     4060                                                peak[8],peak[9],peak[11],peak[12],peak[13],peak[14],I100[ipk]))
     4061                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
     4062                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
     4063                file.close()
     4064        finally:
     4065            dlg.Destroy()
     4066       
     4067    def OnExportPDF(self,event):
     4068        #need S(Q) and G(R) to be saved here - probably best from selection?
     4069        names = G2pdG.GetFileList(self,'PDF')
     4070        exports = []
     4071        if names:
     4072            od = {'label_1':'Export I(Q)','value_1':False,'label_2':'Export S(Q)','value_2':False,
     4073                  'label_3':'Export F(Q)','value_3':False,'label_4':'Export G(R)','value_4':True,
     4074                  'label_5':'Make G(R) for pdfGUI','value_5':False}
     4075            dlg = G2G.G2MultiChoiceDialog(self,'Select','PDF patterns to export',names,extraOpts=od)
     4076            if dlg.ShowModal() == wx.ID_OK:
     4077                sel = dlg.GetSelections()
     4078                for x in sel:
     4079                    exports.append(names[x])
     4080            dlg.Destroy()
     4081        if exports:
     4082            PDFsaves = [od['value_1'],od['value_2'],od['value_3'],od['value_4'],od['value_5']]
     4083            G2IO.PDFSave(self,exports,PDFsaves)
     4084       
     4085    def OnMakePDFs(self,event):
     4086        '''Sets up PDF data structure filled with defaults; if found chemical formula is inserted
     4087        so a default PDF can be made.
     4088        '''
     4089        sind = lambda x: math.sin(x*math.pi/180.)
     4090        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
     4091        tof2q = lambda t,C:2.0*math.pi*C/t
     4092        TextList = []
     4093        ElLists = []
     4094        Qlimits = []
     4095        Names = []
     4096        if self.PatternTree.GetCount():
     4097            id, cookie = self.PatternTree.GetFirstChild(self.root)
     4098            while id:
     4099                name = self.PatternTree.GetItemText(id)
     4100                Names.append(name)
     4101                if 'PWDR' in name:
     4102                    TextList.append(name)
     4103                    Data = self.PatternTree.GetItemPyData(id)[1]
     4104                    pwdrMin = np.min(Data[1])
     4105                    Comments = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,id,'Comments'))
     4106                    Parms = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,id,'Instrument Parameters'))[0]
     4107                    fullLimits = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,id,'Limits'))[0]
     4108                    if 'C' in Parms['Type'][0]:
     4109                        wave = G2mth.getWave(Parms)
     4110                        qMax = tth2q(fullLimits[1],wave)
     4111                    else:   #'T'of
     4112                        qMax = tof2q(fullLimits[0],Parms['difC'][1])
     4113                    Qlimits.append([0.9*qMax,qMax])
     4114                    ElList = {}
     4115                    sumnum = 1.
     4116                    for item in Comments:           #grab chemical formula from Comments
     4117                        if 'formula' in item[:15].lower():
     4118                            formula = item.split('=')[1].split()
     4119                            try:
     4120                                elems = formula[::2]
     4121                                nums = formula[1::2]
     4122                                Formula = zip(elems,nums)
     4123                                sumnum = 0.
     4124                                for [elem,num] in Formula:
     4125                                    ElData = G2elem.GetElInfo(elem,Parms)
     4126                                    ElData['FormulaNo'] = float(num)
     4127                                    sumnum += float(num)
     4128                                    ElList[elem] = ElData
     4129                               
     4130                            except ValueError:
     4131                                ElData = G2elem.GetElInfo(formula[0],Parms)
     4132                                ElData['FormulaNo'] = 1.0
     4133                                ElList[elem] = ElData
     4134                    ElLists.append(ElList)
     4135                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
     4136            if len(TextList) < 1:
     4137                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
     4138                return
     4139            dlg = G2G.G2MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
     4140            try:
     4141                if dlg.ShowModal() == wx.ID_OK:
     4142                    for i in dlg.GetSelections():
     4143                        PDFnames = GetPatternTreeDataNames(self,['PDF ',])
     4144                        G2obj.CreatePDFitems(self,TextList[i],ElLists[i],Qlimits[i],sumnum,pwdrMin,PDFnames)
     4145                for item in self.ExportPDF: item.Enable(True)
     4146            finally:
     4147                dlg.Destroy()
     4148               
     4149    def GetPWDRdatafromTree(self,PWDRname):
     4150        ''' Returns powder data from GSASII tree
     4151
     4152        :param str PWDRname: a powder histogram name as obtained from
     4153          :meth:`GSASIIstruct.GetHistogramNames`
     4154
     4155        :returns: PWDRdata = powder data dictionary with
     4156          Powder data arrays, Limits, Instrument Parameters,
     4157          Sample Parameters           
     4158        '''
     4159        PWDRdata = {}
     4160        try:
     4161            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
     4162        except ValueError:
     4163            PWDRdata['wtFactor'] = 1.0
     4164        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
     4165        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,PWDRname,'Limits'))
     4166        PWDRdata['Background'] = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,PWDRname,'Background'))
     4167        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
     4168        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
     4169        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
     4170        if 'ranId' not in PWDRdata:  # patch, add a random Id
     4171            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
     4172        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
     4173            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
     4174        return PWDRdata
     4175
     4176    def GetHKLFdatafromTree(self,HKLFname):
     4177        ''' Returns single crystal data from GSASII tree
     4178
     4179        :param str HKLFname: a single crystal histogram name as obtained
     4180          from
     4181          :meth:`GSASIIstruct.GetHistogramNames`
     4182
     4183        :returns: HKLFdata = single crystal data list of reflections
     4184
     4185        '''
     4186        HKLFdata = {}
     4187        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
     4188#        try:
     4189#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
     4190#        except ValueError:
     4191#            HKLFdata['wtFactor'] = 1.0
     4192        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
     4193        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
     4194        return HKLFdata
     4195       
     4196    def GetPhaseData(self):
     4197        '''Returns a dict with defined phases.
     4198        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
     4199        get same info from GPX file.
     4200        '''
     4201        phaseData = {}
     4202        if GetPatternTreeItemId(self,self.root,'Phases'):
     4203            sub = GetPatternTreeItemId(self,self.root,'Phases')
     4204        else:
     4205            print 'no phases found in GetPhaseData'
     4206            sub = None
     4207        if sub:
     4208            item, cookie = self.PatternTree.GetFirstChild(sub)
     4209            while item:
     4210                phaseName = self.PatternTree.GetItemText(item)
     4211                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
     4212                if 'ranId' not in phaseData[phaseName]:
     4213                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
     4214                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     4215        return phaseData
     4216
     4217    def GetPhaseInfofromTree(self):
     4218        '''Get the phase names and their rId values,
     4219        also the histograms used in each phase.
     4220
     4221        :returns: (phaseRIdList, usedHistograms) where
     4222
     4223          * phaseRIdList is a list of random Id values for each phase
     4224          * usedHistograms is a dict where the keys are the phase names
     4225            and the values for each key are a list of the histogram names
     4226            used in each phase.
     4227        '''
     4228        phaseRIdList = []
     4229        usedHistograms = {}
     4230        sub = GetPatternTreeItemId(self,self.root,'Phases')
     4231        if sub:
     4232            item, cookie = self.PatternTree.GetFirstChild(sub)
     4233            while item:
     4234                phaseName = self.PatternTree.GetItemText(item)
     4235                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
     4236                if ranId: phaseRIdList.append(ranId)
     4237                data = self.PatternTree.GetItemPyData(item)
     4238                UseList = data['Histograms']
     4239                usedHistograms[phaseName] = UseList.keys()
     4240                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     4241        return phaseRIdList,usedHistograms
     4242
     4243    def GetPhaseNames(self):
     4244        '''Returns a list of defined phases.
     4245        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
     4246        get same info from GPX file.
     4247        '''
     4248        phaseNames = []
     4249        if GetPatternTreeItemId(self,self.root,'Phases'):
     4250            sub = GetPatternTreeItemId(self,self.root,'Phases')
     4251        else:
     4252            print 'no phases found in GetPhaseNames'
     4253            sub = None
     4254        if sub:
     4255            item, cookie = self.PatternTree.GetFirstChild(sub)
     4256            while item:
     4257                phase = self.PatternTree.GetItemText(item)
     4258                phaseNames.append(phase)
     4259                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
     4260        return phaseNames
     4261   
     4262    def GetHistogramNames(self,hType):
     4263        """ Returns a list of histogram names found in the GSASII data tree
     4264        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
     4265        get same info from GPX file.
     4266       
     4267        :param str hType: list of histogram types
     4268        :return: list of histogram names
     4269       
     4270        """
     4271        HistogramNames = []
     4272        if self.PatternTree.GetCount():
     4273            item, cookie = self.PatternTree.GetFirstChild(self.root)
     4274            while item:
     4275                name = self.PatternTree.GetItemText(item)
     4276                if name[:4] in hType:
     4277                    HistogramNames.append(name)       
     4278                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
     4279
     4280        return HistogramNames
     4281                   
     4282    def GetUsedHistogramsAndPhasesfromTree(self):
     4283        ''' Returns all histograms that are found in any phase
     4284        and any phase that uses a histogram.
     4285        This also assigns numbers to used phases and histograms by the
     4286        order they appear in the file.
     4287        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
     4288        get same info from GPX file.
     4289
     4290        :returns: (Histograms,Phases)
     4291
     4292            * Histograms = dictionary of histograms as {name:data,...}
     4293            * Phases = dictionary of phases that use histograms
     4294        '''
     4295        Histograms = {}
     4296        Phases = {}
     4297        phaseNames = self.GetPhaseNames()
     4298        phaseData = self.GetPhaseData()
     4299        histoList = self.GetHistogramNames(['PWDR','HKLF'])
     4300
     4301        for phase in phaseData:
     4302            Phase = phaseData[phase]
     4303            pId = phaseNames.index(phase)
     4304            Phase['pId'] = pId
     4305            if Phase['Histograms']:
     4306                if phase not in Phases:
     4307                    Phases[phase] = Phase
     4308                for hist in Phase['Histograms']:
     4309                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
     4310                        Phase['Histograms'][hist]['Use'] = True         
     4311                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
     4312                        item = GetPatternTreeItemId(self,self.root,hist)
     4313                        if item:
     4314                            if 'PWDR' in hist[:4]:
     4315                                Histograms[hist] = self.GetPWDRdatafromTree(item)
     4316                            elif 'HKLF' in hist[:4]:
     4317                                Histograms[hist] = self.GetHKLFdatafromTree(item)
     4318                            hId = histoList.index(hist)
     4319                            Histograms[hist]['hId'] = hId
     4320                        else: # would happen if a referenced histogram were renamed or deleted
     4321                            print('For phase "'+phase+
     4322                                  '" unresolved reference to histogram "'+hist+'"')
     4323        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
     4324        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
     4325        return Histograms,Phases
     4326       
     4327    def MakeLSParmDict(self):
     4328        '''Load all parameters used for computation from the tree into a
     4329        dict of paired values [value, refine flag]. Note that this is
     4330        different than the parmDict used in the refinement, which only has
     4331        values.
     4332
     4333        Note that similar things are done in
     4334        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
     4335        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
     4336        a GPX file).
     4337
     4338        :returns: (parmDict,varyList) where:
     4339
     4340         * parmDict is a dict with values and refinement flags
     4341           for each parameter and
     4342         * varyList is a list of variables (refined parameters).
     4343        '''
     4344        parmDict = {}
     4345        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
     4346        for phase in Phases:
     4347            if 'pId' not in Phases[phase]:
     4348                self.ErrorDialog('View parameter error','You must run least squares at least once')
     4349                raise Exception,'No pId for phase '+phase
     4350        rigidbodyDict = self.PatternTree.GetItemPyData(   
     4351            GetPatternTreeItemId(self,self.root,'Rigid bodies'))
     4352        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
     4353        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
     4354        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
     4355        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
     4356        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
     4357        varyList = rbVary+phaseVary+hapVary+histVary
     4358        parmDict.update(rbDict)
     4359        parmDict.update(phaseDict)
     4360        parmDict.update(hapDict)
     4361        parmDict.update(histDict)
     4362        for parm in parmDict:
     4363            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
     4364                'Omega','Chi','Phi','nDebye','nPeaks']:
     4365                parmDict[parm] = [parmDict[parm],'-']
     4366            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
     4367                parmDict[parm] = [parmDict[parm],'-']
     4368            elif parm in varyList:
     4369                parmDict[parm] = [parmDict[parm],'T']
     4370            else:
     4371                parmDict[parm] = [parmDict[parm],'F']
     4372        # for i in parmDict: print i,'\t',parmDict[i]
     4373        # fl = open('parmDict.dat','wb')
     4374        # import cPickle
     4375        # cPickle.dump(parmDict,fl,1)
     4376        # fl.close()
     4377        return parmDict,varyList
     4378
     4379    def OnShowLSParms(self,event):
     4380        '''Displays a window showing all parameters in the refinement.
     4381        Called from the Calculate/View LS Parms menu.
     4382        '''
     4383        try:
     4384            parmDict,varyList = self.MakeLSParmDict()
     4385        except:
     4386            print('Error retrieving parameters')
     4387            return
     4388        parmValDict = {}
     4389        for i in parmDict:
     4390            parmValDict[i] = parmDict[i][0]
     4391           
     4392        reqVaryList = tuple(varyList) # save requested variables
     4393        try:
     4394            # process constraints
     4395            sub = GetPatternTreeItemId(self,self.root,'Constraints')
     4396            Constraints = self.PatternTree.GetItemPyData(sub)
     4397            constList = []
     4398            for item in Constraints:
     4399                if item.startswith('_'): continue
     4400                constList += Constraints[item]
     4401            G2mv.InitVars()
     4402            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
     4403            groups,parmlist = G2mv.GroupConstraints(constrDict)
     4404            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
     4405            G2mv.Map2Dict(parmValDict,varyList)
     4406        except:
     4407            pass
     4408        dlg = G2G.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
     4409        dlg.ShowModal()
     4410        dlg.Destroy()
     4411
     4412    def OnRefine(self,event):
     4413        '''Perform a refinement.
     4414        Called from the Calculate/Refine menu.
     4415        '''
     4416        Id = GetPatternTreeItemId(self,self.root,'Sequential results')
     4417        if Id:
     4418            dlg = wx.MessageDialog(
     4419                self,
     4420                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
     4421                'Remove sequential results?',wx.OK|wx.CANCEL)
     4422            if dlg.ShowModal() == wx.ID_OK:
     4423                self.PatternTree.Delete(Id)
     4424                dlg.Destroy()
     4425            else:
     4426                dlg.Destroy()
     4427                return
     4428        self.OnFileSave(event)
     4429        # check that constraints are OK here
     4430        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
     4431        if errmsg:
     4432            self.ErrorDialog('Refinement error',errmsg)
     4433            return
     4434        if warnmsg:
     4435            print('Conflict between refinment flag settings and constraints:\n'+
     4436                warnmsg+'\nRefinement not possible')
     4437            self.ErrorDialog('Refinement Flag Error',
     4438                'Conflict between refinement flag settings and constraints:\n'+
     4439                warnmsg+'\nRefinement not possible')
     4440            return
     4441        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0,
     4442            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
     4443            parent=self)
     4444        Size = dlg.GetSize()
     4445        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
     4446            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
     4447        dlg.CenterOnParent()
     4448        Rw = 100.00
     4449        self.SaveTreeSetting()
     4450        self.PatternTree.SaveExposedItems()       
     4451        try:
     4452            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)    #Msg is Rvals dict if Ok=True
     4453        finally:
     4454            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
     4455            dlg.Destroy()
     4456            wx.Yield()
     4457        if OK:
     4458            Rw = Msg['Rwp']
     4459            lamMax = Msg.get('lamMax',0.001)
     4460            lst = os.path.splitext(os.path.abspath(self.GSASprojectfile))[0]
     4461            text = u'Detailed results are in '+lst+'.lst\n\nLoad new result?'
     4462            if lamMax >= 10.:
     4463                text += '\nWARNING: Steepest descents dominates;'+   \
     4464                ' minimum may not have been reached\nor result may be false minimum.'+  \
     4465                ' You should reconsider your parameter suite'
     4466            dlg2 = wx.MessageDialog(self,text,'Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
     4467            try:
     4468                if dlg2.ShowModal() == wx.ID_OK:
     4469                    self.PatternTree.DeleteChildren(self.root)
     4470                    self.HKL = []
     4471                    G2IO.ProjFileOpen(self,False)
     4472                    self.PatternTree.RestoreExposedItems()       
     4473                    self.ResetPlots()
     4474            finally:
     4475                dlg2.Destroy()
     4476        else:
     4477            self.ErrorDialog('Refinement error',Msg)
     4478       
     4479    def SaveTreeSetting(self):
     4480        'Save the last tree setting'
     4481        oldId =  self.PatternTree.GetSelection()        #retain current selection
     4482        oldPath = self.GetTreeItemsList(oldId)
     4483        self.lastTreeSetting = oldPath
     4484        # note that for reasons unclear, it does not seem necessary to reload the Atoms tab
     4485        #parentName = ''
     4486        #tabId = None
     4487        # parentId = self.PatternTree.GetItemParent(oldId)
     4488        # if parentId:
     4489        #     parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
     4490        #     if 'Phases' in parentName:
     4491        #         tabId = self.dataDisplay.GetSelection()
     4492        #self.lastTreeSetting = oldPath,tabId
     4493        #GSASIIpath.IPyBreak()
     4494       
     4495    def TestResetPlot(self,event):
     4496        '''Debug code to test cleaning up plots after a refinement'''
     4497        #for i in range(self.G2plotNB.nb.GetPageCount()):
     4498        #    [self.G2plotNB.nb.GetPageText(i)
     4499        # save current tree item and (if needed) atoms tab
     4500        self.SaveTreeSetting()
     4501        self.ResetPlots()
     4502       
     4503    def ResetPlots(self):
     4504        '''This reloads the current tree item, often drawing a plot. It refreshes any plots
     4505        that have registered a refresh routine (see G2plotNB.RegisterRedrawRoutine)
     4506        and deletes all plots that have not been refreshed and
     4507        require one (see G2plotNB.SetNoDelete).
     4508        '''
     4509        lastRaisedPlotTab = self.G2plotNB.lastRaisedPlotTab # save the last page saved
     4510        #print 'lastRaisedPlotTab=',lastRaisedPlotTab
     4511        self.G2plotNB.lastRaisedPlotTab = None
     4512        # mark displayed plots as invalid
     4513        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
     4514            frame.plotInvalid = True
     4515        # reload current tree item, triggering the routine to redraw the data window and possibly a plot
     4516        #oldPath,tabId = self.lastTreeSetting
     4517        oldPath = self.lastTreeSetting
     4518        Id = self.root
     4519        for txt in oldPath:
     4520            Id = GetPatternTreeItemId(self, Id, txt)
     4521        self.PickIdText = None  #force reload of page
     4522        if Id:
     4523            self.PickId = Id
     4524            self.PatternTree.SelectItem(Id)
     4525        # update other self-updating plots
     4526#        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
     4527#            if frame.plotInvalid and frame.replotFunction:
     4528#                frame.replotFunction(*frame.replotArgs,**frame.replotKWargs)
     4529        # delete any remaining plots that are still invalid and need a refresh
     4530        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
     4531            if frame.plotInvalid and frame.plotRequiresRedraw:
     4532                self.G2plotNB.Delete(lbl)
     4533        # put the previously last-raised tab on top, if present. If not, use the one corresponding to
     4534        # the last tree item to be selected
     4535        wx.CallAfter(self.G2plotNB.RaiseLastPage,lastRaisedPlotTab,self.G2plotNB.lastRaisedPlotTab)
     4536       
     4537    def OnSeqRefine(self,event):
     4538        '''Perform a sequential refinement.
     4539        Called from the Calculate/Sequential refine menu.
     4540        '''
     4541        Id = GetPatternTreeItemId(self,self.root,'Sequential results')
     4542        if not Id:
     4543            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
     4544            self.PatternTree.SetItemPyData(Id,{})           
     4545        self.G2plotNB.Delete('Sequential refinement')    #clear away probably invalid plot
     4546        Controls = self.PatternTree.GetItemPyData(GetPatternTreeItemId(self,self.root, 'Controls'))
     4547        if not Controls.get('Seq Data'):
     4548            print('Error: a sequential refinement has not been set up')
     4549            return
     4550        Controls['ShowCell'] = True
     4551        self.OnFileSave(event)
     4552        # check that constraints are OK here
     4553        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
     4554        if errmsg:
     4555            self.ErrorDialog('Refinement error',errmsg)
     4556            return
     4557        if warnmsg:
     4558            print('Conflict between refinment flag settings and constraints:\n'+
     4559                  warnmsg+'\nRefinement not possible')
     4560            self.ErrorDialog('Refinement Flag Error',
     4561                             'Conflict between refinment flag settings and constraints:\n'+
     4562                             warnmsg+'\nRefinement not possible')
     4563            return
     4564        self.PatternTree.SaveExposedItems()       
     4565        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0,
     4566            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
     4567            parent=self)           
     4568        Size = dlg.GetSize()
     4569        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
     4570            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
     4571        dlg.CenterOnParent()
     4572        try:
     4573            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg,G2plt.SequentialPlotP