Source code for diffpy.pdfgui.gui.temperatureseriespanel

#!/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:06:35 2019

import os.path
import re

import wx

from diffpy.pdfgui.control.pdfguimacros import makeTemperatureSeries
from diffpy.pdfgui.gui import tooltips
from diffpy.pdfgui.gui.pdfpanel import PDFPanel
from diffpy.pdfgui.gui.wxextensions.listctrls import AutoWidthListCtrl
from diffpy.pdfgui.utils import numericStringSort


[docs] class TemperatureSeriesPanel(wx.Panel, PDFPanel): def __init__(self, *args, **kwds): PDFPanel.__init__(self) # begin wxGlade: TemperatureSeriesPanel.__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\ntemperatues below. If you have not set up a fit to be the template\nfor the series, hit cancel and rerun this macro once a fit has been\ncreated.", # noqa: E501 ) 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_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.buttonUp.SetBitmapLabel(wx.ArtProvider.GetBitmap(wx.ART_GO_UP)) self.buttonDown.SetBitmapLabel(wx.ArtProvider.GetBitmap(wx.ART_GO_DOWN)) self.__customProperties() def __customProperties(self): """Set the custom properties.""" self.fit = None self.reverse = False # Reverse the sort? self.fullpath = "." self.datasets = [] # Contains (temperature, filename) tuples # temperature is a float and comes first for easy sorting self.listCtrlFiles.InsertColumn(0, "Temperature") self.listCtrlFiles.InsertColumn(1, "Data Set") self.listCtrlFiles.SetColumnWidth(0, -2) # Define tooltips. self.setToolTips(tooltips.temperatureseriespanel) return
[docs] def onEndLabelEdit(self, event): # wxGlade: TemperatureSeriesPanel.<event_handler> """Update the temperature in the datasets.""" index = event.GetIndex() text = event.GetText() temperature = 300.0 try: temperature = float(text) except ValueError: event.Veto() return if temperature <= 0: event.Veto() return # update the internal information self.datasets[index][0] = temperature self.reverse = False return
[docs] def onOK(self, event): # wxGlade: TemperatureSeriesPanel.<event_handler> """Let's go!""" paths = [tp[1] for tp in self.datasets] temperatures = [tp[0] for tp in self.datasets] org = makeTemperatureSeries(self.mainFrame.control, self.fit, paths, temperatures) self.treeCtrlMain.ExtendProjectTree(org, clear=False) self.mainFrame.needsSave() self.onCancel(event) return
[docs] def onCancel(self, event): # wxGlade: TemperatureSeriesPanel.<event_handler> """Let's go, but not actually do anything.""" self.mainFrame.setMode("fitting") self.treeCtrlMain.UnselectAll() self.mainFrame.switchRightPanel("blank") return
[docs] def onUp(self, event): # wxGlade: TemperatureSeriesPanel.<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: TemperatureSeriesPanel.<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: TemperatureSeriesPanel.<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|*" # noqa: E501 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 temperatures. Default to 300.0 newdatasets = [] for path in paths: self.fullpath = path self.mainFrame.workpath = os.path.dirname(path) # Look for the temperature in the filename temperature = 300.0 rx = {"f": r"(?:\d+(?:\.\d*)?|\d*\.\d+)"} # Search for T123, t123, Temp123, temp123, 123k, 123K. # Some filenames fool this, e.g. "test1.dat" will match '1' since it # is preceded by a 't'. # Is there a better regexp? Probably... regexp = ( r"""(?:[Tt](?:emp(?:erature)?)?(%(f)s))| (?:(?<![a-zA-Z0-9])(%(f)s)[Kk]) """ % rx ) res = re.search(regexp, os.path.basename(path), re.VERBOSE) if res: groups = res.groups() if groups[0] is not None: temperature = float(res.groups()[0]) else: temperature = float(res.groups()[1]) 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 temperature regexp = r"\b(?:temp|temperature|T)\ *=\ *(%(f)s)\b" % rx res = re.search(regexp, header) if res: temperature = float(res.groups()[0]) # Add the new path if temperature <= 0: temperature = 300.0 newdatasets.append([temperature, path]) # DONT Sort the new paths according to temperature # newdatasets.sort() self.datasets.extend(newdatasets) self.fillList() return
[docs] def onDelete(self, event): # wxGlade: TemperatureSeriesPanel.<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 onColClick(self, event): # wxGlade: TemperatureSeriesPanel.<event_handler> """Sort by temperature.""" column = event.GetColumn() # sort by temperature if column == 0: sortkey = lambda tf: float(tf[0]) # noqa: E731 # sort by filename with numerical comparison of digits elif column == 1: filenames = [f for t, f in self.datasets] numericStringSort(filenames) order = dict(zip(filenames, range(len(filenames)))) sortkey = lambda tf: order[tf[1]] # noqa: E731 # ignore unhandled columns else: return self.datasets.sort(key=sortkey, reverse=self.reverse) self.reverse = not self.reverse self.fillList() return
# Utility functions
[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 temperature, filename in self.datasets: shortname = "..." + filename[idx:] # index = self.listCtrlFiles.InsertItem(sys.maxsize, str(temperature)) #doesn't work for windows index = self.listCtrlFiles.InsertItem(100000, str(temperature)) 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 TemperatureSeriesPanel