Source code for diffpy.pdfgui.gui.dopingseriespanel

#!/usr/bin/env python
# -*- coding: ISO-8859-1 -*-
##############################################################################
#
# PDFgui            by DANSE Diffraction group
#                   Simon J. L. Billinge
#                   (c) 2006 trustees of the Michigan State University.
#                   All rights reserved.
#
# File coded by:    Chris Farrow
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE.txt for license information.
#
##############################################################################

# generated by wxGlade 0.9.3 on Fri Jul 19 16:14:31 2019

import os.path
import re

import wx

from diffpy.pdfgui.control.pdfguimacros import makeDopingSeries
from diffpy.pdfgui.gui import tooltips
from diffpy.pdfgui.gui.pdfpanel import PDFPanel
from diffpy.pdfgui.gui.wxextensions.listctrls import AutoWidthListCtrl
from diffpy.pdfgui.gui.wxextensions.validators import ALPHA_ONLY, TextValidator


[docs] class DopingSeriesPanel(wx.Panel, PDFPanel): def __init__(self, *args, **kwds): PDFPanel.__init__(self) # begin wxGlade: DopingSeriesPanel.__init__ kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL wx.Panel.__init__(self, *args, **kwds) sizer_1 = wx.BoxSizer(wx.VERTICAL) self.instructionsLabel = wx.StaticText( self, wx.ID_ANY, "Select a fit from the tree on the left then add datasets and " + "assign\ndoping elements and values below. " + "If you have not set up a fit to be\nthe template for the series, " + "hit cancel and rerun this macro once a\nfit has been created.", ) self.instructionsLabel.SetFont( wx.Font( 10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, "Sans", ) ) sizer_1.Add(self.instructionsLabel, 0, wx.ALL | wx.EXPAND, 5) sizer_6 = wx.BoxSizer(wx.HORIZONTAL) sizer_1.Add(sizer_6, 0, wx.EXPAND, 0) self.labelBaseElement = wx.StaticText(self, wx.ID_ANY, "Base element") sizer_6.Add(self.labelBaseElement, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) self.textCtrlBaseElement = wx.TextCtrl(self, wx.ID_ANY, "") sizer_6.Add(self.textCtrlBaseElement, 0, wx.ALL, 5) self.labelDopant = wx.StaticText(self, wx.ID_ANY, "Dopant") sizer_6.Add(self.labelDopant, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) self.textCtrlDopant = wx.TextCtrl(self, wx.ID_ANY, "") sizer_6.Add(self.textCtrlDopant, 0, wx.ALL, 5) sizer_2 = wx.BoxSizer(wx.VERTICAL) sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) sizer_4 = wx.BoxSizer(wx.HORIZONTAL) sizer_2.Add(sizer_4, 1, wx.EXPAND, 0) self.listCtrlFiles = AutoWidthListCtrl( self, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_EDIT_LABELS | wx.LC_REPORT ) sizer_4.Add(self.listCtrlFiles, 1, wx.ALL | wx.EXPAND, 5) sizer_5 = wx.BoxSizer(wx.VERTICAL) sizer_4.Add(sizer_5, 0, wx.EXPAND, 0) sizer_5.Add((0, 0), 1, 0, 0) self.buttonUp = wx.BitmapButton(self, wx.ID_ANY, wx.NullBitmap) self.buttonUp.SetSize(self.buttonUp.GetBestSize()) sizer_5.Add(self.buttonUp, 0, wx.ALL, 5) self.buttonDown = wx.BitmapButton(self, wx.ID_ANY, wx.NullBitmap) self.buttonDown.SetSize(self.buttonDown.GetBestSize()) sizer_5.Add(self.buttonDown, 0, wx.ALL, 5) sizer_5.Add((0, 0), 1, 0, 0) grid_sizer_1 = wx.GridSizer(1, 2, 10, 10) sizer_1.Add(grid_sizer_1, 0, wx.ALL, 5) self.buttonAdd = wx.Button(self, wx.ID_ADD, "Add") grid_sizer_1.Add(self.buttonAdd, 0, 0, 0) self.buttonDelete = wx.Button(self, wx.ID_DELETE, "Delete") grid_sizer_1.Add(self.buttonDelete, 0, 0, 0) self.static_line_1 = wx.StaticLine(self, wx.ID_ANY) sizer_1.Add(self.static_line_1, 0, wx.EXPAND, 0) sizer_3 = wx.BoxSizer(wx.HORIZONTAL) sizer_1.Add(sizer_3, 0, wx.EXPAND, 0) sizer_3.Add((20, 20), 1, wx.EXPAND, 0) self.goButton = wx.Button(self, wx.ID_OK, "OK") sizer_3.Add(self.goButton, 0, wx.ALL, 5) self.cancelButton = wx.Button(self, wx.ID_CANCEL, "Cancel") sizer_3.Add(self.cancelButton, 0, wx.ALL, 5) self.SetSizer(sizer_1) sizer_1.Fit(self) self.Layout() self.Bind(wx.EVT_LIST_COL_CLICK, self.onColClick, self.listCtrlFiles) self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.onEndLabelEdit, self.listCtrlFiles) self.Bind(wx.EVT_BUTTON, self.onUp, self.buttonUp) self.Bind(wx.EVT_BUTTON, self.onDown, self.buttonDown) self.Bind(wx.EVT_BUTTON, self.onAdd, self.buttonAdd) self.Bind(wx.EVT_BUTTON, self.onDelete, self.buttonDelete) self.Bind(wx.EVT_BUTTON, self.onOK, self.goButton) self.Bind(wx.EVT_BUTTON, self.onCancel, self.cancelButton) # end wxGlade self.__customProperties() def __customProperties(self): """Set the custom properties.""" self.fit = None self.reverse = False # Reverse the sort? self.fullpath = "" self.datasets = [] # Contains (doping, filename) tuples # doping comes first for easy sorting self.listCtrlFiles.InsertColumn(0, "Doping") self.listCtrlFiles.InsertColumn(1, "Data Set") self.listCtrlFiles.SetColumnWidth(0, -2) # Set the validators self.textCtrlBaseElement.SetValidator(TextValidator(ALPHA_ONLY)) self.textCtrlDopant.SetValidator(TextValidator(ALPHA_ONLY)) # Define tooltips. self.setToolTips(tooltips.dopingseriespanel) return
[docs] def onColClick(self, event): # wxGlade: DopingSeriesPanel.<event_handler> """Sort by doping.""" self.datasets.sort() if self.reverse: self.datasets.reverse() self.reverse = not self.reverse self.fillList() return
[docs] def onEndLabelEdit(self, event): # wxGlade: DopingSeriesPanel.<event_handler> """Update the doping in the datasets.""" index = event.GetIndex() text = event.GetText() doping = 0.0 try: doping = float(text) except ValueError: event.Veto() return if doping < 0 or doping > 1: event.Veto() return # update the internal information self.datasets[index][0] = doping self.reverse = False return
[docs] def onUp(self, event): # wxGlade: DopingSeriesPanel.<event_handler> """Move an item in the list up.""" index = self.listCtrlFiles.GetFirstSelected() if index > 0: temp = self.datasets[index] self.datasets[index] = self.datasets[index - 1] self.datasets[index - 1] = temp self.fillList() self.listCtrlFiles.Select(index - 1) return
[docs] def onDown(self, event): # wxGlade: DopingSeriesPanel.<event_handler> """Move an item in the list down.""" index = self.listCtrlFiles.GetFirstSelected() if index > -1 and index != len(self.datasets) - 1: temp = self.datasets[index] self.datasets[index] = self.datasets[index + 1] self.datasets[index + 1] = temp self.fillList() self.listCtrlFiles.Select(index + 1) return
[docs] def onAdd(self, event): # wxGlade: DopingSeriesPanel.<event_handler> """Append files to the list.""" dir, filename = os.path.split(self.fullpath) if not dir: dir = self.mainFrame.workpath matchstring = ( "PDF data files (*.gr)|*.gr|PDF fit files (*.fgr)|*.fgr|PDF fit files (*.fit)|" "*.fit|PDF calculation files (*.cgr)|*.cgr|PDF calculation files (*.calc)|*.calc|" "All Files|*" ) d = wx.FileDialog( None, "Choose files", dir, "", matchstring, wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE, ) paths = [] if d.ShowModal() == wx.ID_OK: paths = d.GetPaths() d.Destroy() # Assign the doping. Default to 0.0 newdatasets = [] for path in paths: self.fullpath = path self.mainFrame.workpath = os.path.dirname(self.fullpath) # Look for the doping in the filename doping = 0.0 rx = {"f": r"(?:\d+(?:\.\d*)?|\d*\.\d+)"} # Search for x123, X123, doping123, Doping123. # Is there a better regexp? Probably... regexp = r"(?:X|x|Doping|doping)(%(f)s)" % rx res = re.search(regexp, os.path.basename(path)) if res: doping = float(res.groups()[0]) else: # Look in the file infile = open(path, "r") datastring = infile.read() infile.close() # Look for it first in the file res = re.search(r"^#+ start data\s*(?:#.*\s+)*", datastring, re.M) # start_data is position where the first data line starts if res: start_data = res.end() else: res = re.search(r"^[^#]", datastring, re.M) if res: start_data = res.start() else: start_data = 0 header = datastring[:start_data] # parse header to get doping regexp = r"\b(?:x|doping)\ *=\ *(%(f)s)\b" % rx res = re.search(regexp, header) if res: doping = float(res.groups()[0]) # Add the new path if doping < 0: doping = 0.0 newdatasets.append([doping, path]) self.datasets.extend(newdatasets) self.fillList() return
[docs] def onDelete(self, event): # wxGlade: DopingSeriesPanel.<event_handler> """Delete selected files from the list.""" idxlist = [] item = self.listCtrlFiles.GetFirstSelected() while item != -1: idxlist.append(item) item = self.listCtrlFiles.GetNextSelected(item) idxlist.reverse() for item in idxlist: del self.datasets[item] self.fillList() return
[docs] def onOK(self, event): # wxGlade: DopingSeriesPanel.<event_handler> """Let's go!""" dvals = [tp[0] for tp in self.datasets] paths = [tp[1] for tp in self.datasets] base = self.textCtrlBaseElement.GetValue() dopant = self.textCtrlDopant.GetValue() # Value checks will take place in makeDopingSeries org = makeDopingSeries(self.mainFrame.control, self.fit, base, dopant, paths, dvals) self.treeCtrlMain.ExtendProjectTree(org, clear=False) self.mainFrame.needsSave() self.onCancel(event) return
[docs] def onCancel(self, event): # wxGlade: DopingSeriesPanel.<event_handler> """Let's go, but not actually do anything.""" self.mainFrame.setMode("fitting") self.treeCtrlMain.UnselectAll() self.mainFrame.switchRightPanel("blank") return
# Utility functions
[docs] def checkConfiguration(self): """Verify that the dopant and base are elements. More detailed checking is done in the control method. """ from diffpy.pdffit2 import is_element from diffpy.pdfgui.control.controlerrors import ControlValueError base = self.textCtrlBaseElement.GetValue() dopant = self.textCtrlDopant.GetValue() # Make sure that the base and dopant are actual elements base = base.title() dopant = dopant.title() if not is_element(base): raise ControlValueError("'%s' is not an element!" % base) if not is_element(dopant): raise ControlValueError("'%s' is not an element!" % dopant) return
[docs] def fillList(self): """Fill the list with the datasets.""" self.listCtrlFiles.DeleteAllItems() names = [pair[1] for pair in self.datasets] cp = os.path.commonprefix(names) # We want to break at the last path/separator in the common prefix idx = cp.rfind(os.path.sep) if idx == -1: idx = len(cp) for doping, filename in self.datasets: shortname = "..." + filename[idx:] # index = self.listCtrlFiles.InsertItem(sys.maxsize, str(doping)) #doesn't work for windows index = self.listCtrlFiles.InsertItem(100000, str(doping)) # doesn't work for windows self.listCtrlFiles.SetItem(index, 1, shortname) return
# Needed by mainframe
[docs] def treeSelectionUpdate(self, node): """Set the current fit when the tree selection changes.""" nodetype = self.treeCtrlMain.GetNodeType(node) if nodetype == "fit": self.fit = self.treeCtrlMain.GetControlData(node) self.refresh() return
# Required by PDFPanel
[docs] def refresh(self): """Block out OK button if there is no fit. This also blocks OK if the fit has no datasets or structures. """ # We can't rely on Veto to block unwanted tree selections on windows. # So, we have to check for errors here. node = None nodetype = None selections = self.treeCtrlMain.GetSelections() if selections: node = selections[0] nodetype = self.treeCtrlMain.GetNodeType(node) if node and nodetype == "fit" and self.fit and self.fit.hasDataSets() and self.fit.hasStructures(): self.goButton.Enable() else: self.goButton.Enable(False) return
# end of class DopingSeriesPanel