Skip to content

Commit

Permalink
Merge pull request #191 from ebranlard/dev
Browse files Browse the repository at this point in the history
Version 0.5 - Merging dev to main
  • Loading branch information
ebranlard authored Oct 26, 2024
2 parents 4140c24 + 3a29687 commit 07187cb
Show file tree
Hide file tree
Showing 58 changed files with 4,648 additions and 1,880 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: [3.8, 3.9, 3.11]
python-version: [3.8, 3.9, 3.11, 3.12]

steps:
# --- Install steps
Expand All @@ -27,7 +27,7 @@ jobs:
run: |
git fetch --unshallow > /dev/null
git fetch --tags --force > /dev/null
export CURRENT_TAG="v0.4"
export CURRENT_TAG="v0.5"
export CURRENT_DEV_TAG="$CURRENT_TAG-dev"
# BRANCH FAILS
export BRANCH1=`git rev-parse --abbrev-ref HEAD`
Expand Down
1 change: 1 addition & 0 deletions _tools/travis_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
openpyxl
numpy
pandas
xarray
pyarrow # for parquet files
matplotlib
chardet
Expand Down
3 changes: 2 additions & 1 deletion installer.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[Application]
name=pyDatView
version=0.4
version=0.5
entry_point=pydatview:show_sys_args
icon=ressources/pyDatView.ico

Expand Down Expand Up @@ -35,6 +35,7 @@ pypi_wheels =
Pillow==9.1.1
packaging==21.2
fatpack==0.7.3
xarray==2023.2.0

# numpy==1.19.3
# wxPython==4.0.3
Expand Down
85 changes: 85 additions & 0 deletions pydatview/Fields2D.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""
TODO come up with some decent sepcs. Potentially use pandas or xarray
"""
import numpy as np

def extract2Dfields(fo, force=False, **kwargs):
if not hasattr(fo, 'fields2D_tmp') or force:
fo.fields2D_tmp = None
#print('[INFO] Attempting to extract 2D field for file {}'.format(fo.filename))
if not hasattr(fo, 'to2DFields'):
print('[WARN] type {} does not have a `to2DFields` method'.format(type(fo)))
return None
try:
fields = fo.to2DFields(**kwargs)
except:
print('[FAIL] Attempting to extract 2D field for file {}'.format(fo.filename))
return None
if fields is None:
print('[WARN] type {} has a `to2DFields` method but returned None'.format(type(fo)))
return None
# Convert to pydatview datatype for 2d fields
fo.fields2D_tmp = Fields2D(fields)
fo.fields2D_tmp.keys()
print('[ OK ] 2D field computed successfully')
else:
print('[INFO] 2D field already computed for file {}'.format(fo.filename))
if not isinstance(fo.fields2D_tmp, Fields2D):
raise Exception('ImplementationError')

return fo.fields2D_tmp


class Fields2D():
"""
Fields2D is a list of xarray, readonly
"""
def __init__(self, ds=None):
if ds is None:
ds = []
self.ds = ds
self._keys = None

def __repr__(self):
s='<{} object>:\n'.format(type(self).__name__)
s+='|Main attributes:\n'
s+='| - ds: {}\n'.format(self.ds)
s+='| - _keys: {}\n'.format(self._keys)
return s

def keys(self):
if self._keys is not None:
return self._keys
keys =[]
variables = self.ds.variables
# Filter variables based on dimensions (r, t)
dims = np.unique(np.array([self.ds[var].dims for var in variables], dtype=object))
dims2d = [d for d in dims if len(d)==2]
for D in dims2d:
for var in variables:
if self.ds[var].dims==(D[0],D[1]):
keys.append(var)
self._keys = keys
return keys

def loc(self, svar):
try:
i1, i2 = self.ds[svar].dims
except:
raise IndexError('Variable {} not found in field'.format(svar))
sx = i1
sy = i2
if 'unit' in self.ds[i1].attrs.keys():
sx += ' ['+ self.ds[i1].attrs['unit'] + ']'
if 'unit' in self.ds[i2].attrs.keys():
sy += ' ['+ self.ds[i2].attrs['unit'] + ']'
fieldname = svar
if 'unit' in self.ds[svar].attrs.keys():
fieldname += ' ['+ self.ds[svar].attrs['unit'] + ']'
return {'M': self.ds[svar].values, 'x':self.ds[i1].values, 'y':self.ds[i2].values, 'sx':sx, 'sy':sy, 'fieldname':fieldname}

def iloc(self, i):
var = self._keys[i]
return self.loc(var)

3 changes: 3 additions & 0 deletions pydatview/GUICommon.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ def __init__(self, parent, title, message):
text = wx.TextCtrl(self, style=wx.TE_READONLY|wx.BORDER_NONE|wx.TE_MULTILINE|wx.TE_AUTO_URL)
text.SetValue(message)
text.SetBackgroundColour(wx.SystemSettings.GetColour(4))
# box_sizer = wx.BoxSizer(wx.VERTICAL)
# box_sizer.Add(text)
# self.SetSizerAndFit(box_sizer)
self.ShowModal()
self.Destroy()
MessageBox(parent, 'About', message)
Expand Down
97 changes: 97 additions & 0 deletions pydatview/GUIFields1D.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
"""
import wx
from pydatview.GUIPlotPanel import PlotPanel
from pydatview.GUIInfoPanel import InfoPanel
from pydatview.GUISelectionPanel import SelectionPanel, SEL_MODES_ID
# from pydatview.GUISelectionPanel import SelectionPanel,SEL_MODES,SEL_MODES_ID
# from pydatview.GUISelectionPanel import ColumnPopup,TablePopup
# from pydatview.GUIPipelinePanel import PipelinePanel
# from pydatview.GUIToolBox import GetKeyString, TBAddTool
# from pydatview.Tables import TableList, Table


SIDE_COL = [160,160,300,420,530]
SIDE_COL_LARGE = [200,200,360,480,600]
BOT_PANL =85

class Fields1DPanel(wx.SplitterWindow): # TODO Panel

def __init__(self, parent, mainframe):
# Superclass constructor
super(Fields1DPanel, self).__init__(parent)
# Data
self.parent = parent
self.mainframe = mainframe

self.vSplitter = self # Backward compatibility

# --- Create a selPanel, plotPanel and infoPanel
mode = SEL_MODES_ID[mainframe.comboMode.GetSelection()]
self.selPanel = SelectionPanel(self.vSplitter, mainframe.tabList, mode=mode, mainframe=mainframe)
self.tSplitter = wx.SplitterWindow(self.vSplitter)
#self.tSplitter.SetMinimumPaneSize(20)
self.infoPanel = InfoPanel(self.tSplitter, data=mainframe.data['infoPanel'])
self.plotPanel = PlotPanel(self.tSplitter, self.selPanel, infoPanel=self.infoPanel, pipeLike=mainframe.pipePanel, data=mainframe.data['plotPanel'])
self.livePlotFreezeUnfreeze() # Dont enable panels if livePlot is not allowed
self.tSplitter.SetSashGravity(0.9)
self.tSplitter.SplitHorizontally(self.plotPanel, self.infoPanel)
self.tSplitter.SetMinimumPaneSize(BOT_PANL)
self.tSplitter.SetSashGravity(1)
self.tSplitter.SetSashPosition(400)

self.vSplitter.SplitVertically(self.selPanel, self.tSplitter)
self.vSplitter.SetMinimumPaneSize(SIDE_COL[0])
self.tSplitter.SetSashPosition(SIDE_COL[0])


# --- Bind
# The selPanel does the binding, but the callback is stored here because it involves plotPanel... TODO, rethink it
#self.selPanel.bindColSelectionChange(self.onColSelectionChangeCallBack)
self.selPanel.setTabSelectionChangeCallback(mainframe.onTabSelectionChangeTrigger)
self.selPanel.setRedrawCallback(mainframe.redrawCallback)
self.selPanel.setUpdateLayoutCallback(mainframe.mainFrameUpdateLayout)
self.plotPanel.setAddTablesCallback(mainframe.load_dfs)

self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, mainframe.onSashChangeMain, self.vSplitter)

# --- Mainframe backward compatibility
mainframe.selPanel = self.selPanel
mainframe.plotPanel = self.plotPanel
mainframe.infoPanel = self.infoPanel


def updateSashLayout(self, event=None):
# try:
nWind = self.selPanel.splitter.nWindows
if self.Size[0]<=800:
sash=SIDE_COL[nWind]
else:
sash=SIDE_COL_LARGE[nWind]
self.resizeSideColumn(sash)
# except:
# print('[Fail] An error occured in mainFrameUpdateLayout')


# --- Side column
def resizeSideColumn(self,width):
# To force the replot we do an epic unsplit/split...
#self.vSplitter.Unsplit()
#self.vSplitter.SplitVertically(self.selPanel, self.tSplitter)
self.vSplitter.SetMinimumPaneSize(width)
self.vSplitter.SetSashPosition(width)
#self.selPanel.splitter.setEquiSash()

def livePlotFreezeUnfreeze(self):
pass
#if self.cbLivePlot.IsChecked():
# if hasattr(self,'plotPanel'):
# #print('[INFO] Enabling live plot')
# #self.plotPanel.Enable(True)
# self.infoPanel.Enable(True)
#else:
# if hasattr(self,'plotPanel'):
# #print('[INFO] Disabling live plot')
# #self.plotPanel.Enable(False)
# self.infoPanel.Enable(False)

153 changes: 153 additions & 0 deletions pydatview/GUIFields2D.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
"""
"""
import os
import numpy as np
import wx
from wx.lib.splitter import MultiSplitterWindow
# Local
from pydatview.common import ellude_common
from pydatview.common import CHAR
from pydatview.GUICommon import getMonoFont
from pydatview.Fields2D import extract2Dfields
from pydatview.GUIPlot2DPanel import Plot2DPanel

# --------------------------------------------------------------------------------}
# --- Fields 2D Panel
# --------------------------------------------------------------------------------{
class Fields2DPanel(wx.Panel):
def __init__(self, parent, mainframe):
wx.Panel.__init__(self, parent)
# Data
self.parent = parent
self.mainframe = mainframe
self.fileobjects = None

multi_split = MultiSplitterWindow(self)
self.filesPanel = wx.Panel(multi_split)
self.fieldsPanel = wx.Panel(multi_split)
self.canvasPanel = Plot2DPanel(multi_split)

# GUI
self.btExtractFields = wx.Button(self.filesPanel, label=CHAR['compute']+' '+"Extract 2D fields for all", style=wx.BU_EXACTFIT)
self.textArgs = wx.TextCtrl(self.filesPanel, wx.ID_ANY, '', style = wx.TE_PROCESS_ENTER)
self.lbFiles = wx.ListBox(self.filesPanel, style=wx.LB_EXTENDED)
self.lbFields = wx.ListBox(self.fieldsPanel, style=wx.LB_EXTENDED)
self.textArgs.SetValue('DeltaAzi=10') # TODO
self.lbFiles.SetFont(getMonoFont(self))
self.lbFields.SetFont(getMonoFont(self))
self.textArgs.SetFont(getMonoFont(self))

# Layout
sizer_files = wx.BoxSizer(wx.VERTICAL)
sizer_files.Add(self.textArgs, 0, wx.EXPAND | wx.ALL, 1)
sizer_files.Add(self.btExtractFields, 0, wx.EXPAND | wx.ALL, 1)
sizer_files.Add(self.lbFiles, 1, wx.EXPAND | wx.ALL, 1)
self.filesPanel.SetSizer(sizer_files)

sizer_fields = wx.BoxSizer(wx.VERTICAL)
sizer_fields.Add(self.lbFields, 1, wx.EXPAND | wx.ALL, 1)
self.fieldsPanel.SetSizer(sizer_fields)

multi_split.AppendWindow(self.filesPanel, 200)
multi_split.AppendWindow(self.fieldsPanel, 200)
multi_split.AppendWindow(self.canvasPanel)

sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(multi_split, 1, wx.EXPAND)
self.SetSizer(sizer)

# Bind
self.lbFiles.Bind(wx.EVT_LISTBOX, self.on_file_selected)
self.lbFields.Bind(wx.EVT_LISTBOX, self.on_2d_field_selected)
self.btExtractFields.Bind(wx.EVT_BUTTON, self.onExtract)
#self.textArgs.Bind(wx.EVT_TEXT_ENTER, self.onParamChangeEnter)

def cleanGUI(self, event=None):
self.deselect()
self.lbFiles.Clear()
self.lbFields.Clear()
self.canvasPanel.clean_plot()

def deselect(self, event=None):
[self.lbFiles.Deselect(i) for i in self.lbFiles.GetSelections()]
[self.lbFields.Deselect(i) for i in self.lbFields.GetSelections()]

def updateFiles(self, filenames, fileobjects):
self.fileobjects=fileobjects
filenames = [os.path.abspath(f).replace('/','|').replace('\\','|') for f in filenames]
filenames = ellude_common(filenames)
self.lbFiles.Set(filenames)
#self.lbFiles.SetSelection(0)
#self.on_file_selected()

def getArgs(self):
args = self.textArgs.GetValue().split(',')
kwargs={}
for arg in args:
k, v = arg.split('=')
kwargs[k.strip()] = v.strip()
return kwargs

def onExtract(self, event=None):
self.deselect()
kwargs = self.getArgs()
for fo in self.fileobjects:
extract2Dfields(fo, force=True, **kwargs)

def on_file_selected(self, event=None):
self.canvasPanel.clean_plot()

ISelF = self.lbFiles.GetSelections()
kwargs = self.getArgs()
# --- Compute 2d field if not done yet
for iself in ISelF:
file_object = self.fileobjects[iself]
if not hasattr(file_object, 'fields2D_tmp'):
# Computing fields here if not already done for this file
fields = extract2Dfields(file_object, **kwargs)
else:
fields = file_object.fields2D_tmp

iself = ISelF[0]
fieldListByFile=[]
for iself in ISelF:
fields = file_object.fields2D_tmp
if fields is not None:
fieldListByFile.append(fields.keys())
else:
print('[WARN] No 2D fields for this file')

# --- Get common columns
if len(fieldListByFile)>0:
commonCols = fieldListByFile[0]
for i in np.arange(1,len(fieldListByFile)):
commonCols = list( set(commonCols) & set( fieldListByFile[i]))
# Respect order of first
commonCols = [c for c in fieldListByFile[0] if c in commonCols]
#commonCols.sort()
self.lbFields.Set(commonCols)

# Trigger, we select the first field...
if len(commonCols)>0:
self.lbFields.SetSelection(0)
self.on_2d_field_selected()
else:
print('[WARN] No 2D fields')


def on_2d_field_selected(self, event=None):
ISelF = self.lbFiles.GetSelections()
self.canvasPanel.fields=[]
for iself in ISelF :
file_object = self.fileobjects[iself]
ISelC = self.lbFields.GetSelections()
for iselc in ISelC:
sfield = self.lbFields.GetString(iselc)
# Field is a dictionary with keys: M, x, y, sx, sy, fieldname
field = file_object.fields2D_tmp.loc(sfield)
self.canvasPanel.add_field(**field)
self.canvasPanel.update_plot()




Loading

0 comments on commit 07187cb

Please sign in to comment.