"""
Author: HECE - University of Liege, Pierre Archambeau
Date: 2024
Copyright (c) 2024 University of Liege. All rights reserved.
This script and its content are protected by copyright law. Unauthorized
copying or distribution of this file, via any medium, is strictly prohibited.
"""
import wx
import logging
from ..PyTranslate import _
from ..CpGrid import CpGrid
from ._model import wolfpaletteModel
import numpy as np
[docs]
class wolfpalette(wolfpaletteModel, wx.Frame):
"""
Color palette with wxPython GUI support.
Inherits all data/computation logic from :class:`wolfpaletteModel`
and adds wx.Frame initialization, dialog-based file I/O, and CpGrid integration.
"""
def __init__(self, parent=None, title=_('Colormap'), w=100, h=500, nseg=1024):
wolfpaletteModel.__init__(self, nseg=nseg)
[docs]
self.wx_exists = wx.App.Get() is not None
# Call to initialize a general frame
if (self.wx_exists):
wx.Frame.__init__(self, parent, title=title, size=(w, h), style=wx.DEFAULT_FRAME_STYLE)
# ---------------------------------------------------------------
# Overrides with wx dialog support
# ---------------------------------------------------------------
[docs]
def distribute_values(self, minval: float = -99999, maxval: float = -99999, step=0, wx_permitted=True):
""" Distribution of the palette values
:param minval: minimum value
:param maxval: maximum value
:param step: distribution step
:param wx_permitted: if True and wx app exists, show dialogs for missing values
If the step is provided, it takes precedence over the maximum value.
"""
if self.wx_exists and wx_permitted:
if minval == -99999:
dlg = wx.TextEntryDialog(None, _('Minimum value'), value=str(self.values[0]))
ret = dlg.ShowModal()
try:
self.values[0] = float(dlg.GetValue())
except:
logging.warning('Bad value for minimum - No change')
dlg.Destroy()
else:
self.values[0] = minval
if maxval == -99999 and step == 0:
dlg = wx.MessageDialog(None, _('Would you like to set the incremental step value ?'),
style=wx.YES_NO | wx.YES_DEFAULT)
ret = dlg.ShowModal()
dlg.Destroy
if ret == wx.ID_YES:
dlg = wx.TextEntryDialog(None, _('Step value'), value='1')
ret = dlg.ShowModal()
try:
step = float(dlg.GetValue())
except:
logging.warning('Bad value for step - using default value 0.1 m')
step = 0.1
dlg.Destroy()
else:
dlg = wx.TextEntryDialog(None, _('Maximum value'), value=str(self.values[-1]))
ret = dlg.ShowModal()
try:
self.values[-1] = float(dlg.GetValue())
except:
logging.warning('Bad value for maximum - using min value + 1 m')
self.values[-1] = self.values[0] + 1.
dlg.Destroy()
elif maxval != -99999:
self.values[-1] = maxval
# Perform the computation (same as model)
if step == 0:
self.values = np.linspace(self.values[0], self.values[-1], num=self.nb,
endpoint=True, dtype=np.float64)[0:self.nb]
else:
self.values = np.arange(self.values[0], self.values[0]+(self.nb)*step, step, dtype=np.float64)[0:self.nb]
self.fill_segmentdata()
else:
super().distribute_values(minval=minval, maxval=maxval, step=step)
[docs]
def export_image(self, fn='', h_or_v=None, figax=None):
"""
Export image from colormap — with wx.FileDialog fallback when fn is empty.
"""
if fn == '' and self.wx_exists:
file = wx.FileDialog(None, "Choose .pal file", wildcard="png (*.png)|*.png|all (*.*)|*.*", style=wx.FD_SAVE)
if file.ShowModal() == wx.ID_CANCEL:
return
else:
# Retrieve the filename with path
fn = file.GetPath()
if h_or_v is None:
h_or_v = ''
return super().export_image(fn=fn, h_or_v=h_or_v, figax=figax)
[docs]
def readfile(self, *args):
""" Read the palette from a WOLF .pal file — with wx.FileDialog when no path given. """
if len(args) == 0:
if self.wx_exists:
# open a dialog box
file = wx.FileDialog(None, "Choose .pal file", wildcard="pal (*.pal)|*.pal|all (*.*)|*.*")
if file.ShowModal() == wx.ID_CANCEL:
file.Destroy()
return
else:
# retrieve the filename with path
args = (file.GetPath(),)
file.Destroy()
else:
return
super().readfile(*args)
[docs]
def savefile(self, *args):
"""Save the palette to a WOLF .pal file — with wx.FileDialog when no path given."""
if len(args) == 0:
if self.wx_exists:
# ouverture d'une boîte de dialogue
file = wx.FileDialog(None, "Choose .pal file", wildcard="pal (*.pal)|*.pal|all (*.*)|*.*", style=wx.FD_SAVE)
if file.ShowModal() == wx.ID_CANCEL:
return
else:
# récupération du nom de fichier avec chemin d'accès
args = (file.GetPath(),)
else:
return
super().savefile(*args)
# ---------------------------------------------------------------
# wx-specific lookup methods (return wx.Colour)
# ---------------------------------------------------------------
[docs]
def lookupcolor(self, x):
if x < self.values[0]:
return wx.Colour(self.colormin)
if x > self.values[-1]:
return wx.Colour(self.colormax)
rgba = self.lookupcolor_rgba(x)
return wx.Colour(*rgba)
[docs]
def lookupcolorflt(self, x):
if x < self.values[0]:
return wx.Colour(self.colormin)
if x > self.values[-1]:
return wx.Colour(self.colormax)
# Delegate to model for interpolation (returns list)
return super().lookupcolorflt(x)
[docs]
def lookupcolorrgb(self, x):
if x < self.values[0]:
return wx.Colour(self.colormin)
if x > self.values[-1]:
return wx.Colour(self.colormax)
# Delegate to model for interpolation (returns tuple)
return super().lookupcolorrgb(x)
# ---------------------------------------------------------------
# CpGrid integration (wx-only)
# ---------------------------------------------------------------
[docs]
def fillgrid(self, gridto: CpGrid):
""" Fill a grid with the palette values """
gridto.SetColLabelValue(0, 'Value')
gridto.SetColLabelValue(1, 'R')
gridto.SetColLabelValue(2, 'G')
gridto.SetColLabelValue(3, 'B')
nb = gridto.GetNumberRows()
if len(self.values)-nb > 0:
gridto.AppendRows(len(self.values)-nb)
k = 0
for curv, rgba in zip(self.values, self.colors):
gridto.SetCellValue(k, 0, str(curv))
gridto.SetCellValue(k, 1, str(rgba[0]))
gridto.SetCellValue(k, 2, str(rgba[1]))
gridto.SetCellValue(k, 3, str(rgba[2]))
k += 1
nb = gridto.GetNumberRows()
while k < nb:
gridto.SetCellValue(k, 0, '')
gridto.SetCellValue(k, 1, '')
gridto.SetCellValue(k, 2, '')
gridto.SetCellValue(k, 3, '')
k += 1
[docs]
def updatefromgrid(self, gridfrom: CpGrid):
""" Update the palette based on a grid """
nbl = gridfrom.GetNumberRows()
notnull = 0
for i in range(nbl):
if gridfrom.GetCellValue(i, 0) != '':
notnull += 1
else:
break
if notnull < self.nb:
self.nb = notnull
self.values = self.values[0:notnull]
self.colors = self.colors[0:notnull, :]
else:
self.nb = notnull
oldvalues = self.values
oldcolors = self.colors
self.values = np.zeros(self.nb, dtype=np.float64)
self.colors = np.zeros((self.nb, 4), dtype=int)
self.values[0:len(oldvalues)] = oldvalues
self.colors[0:len(oldcolors), :] = oldcolors
update = False
for k in range(self.nb):
update = update or self.values[k] != float(gridfrom.GetCellValue(k, 0))
update = update or self.colors[k, 0] != float(gridfrom.GetCellValue(k, 1))
update = update or self.colors[k, 1] != float(gridfrom.GetCellValue(k, 2))
update = update or self.colors[k, 2] != float(gridfrom.GetCellValue(k, 3))
self.values[k] = float(gridfrom.GetCellValue(k, 0))
self.colors[k, 0] = int(gridfrom.GetCellValue(k, 1))
self.colors[k, 1] = int(gridfrom.GetCellValue(k, 2))
self.colors[k, 2] = int(gridfrom.GetCellValue(k, 3))
self.fill_segmentdata()
return update