Source code for wolfhece.ui.wolf_array_ui

"""
UI-related classes extracted from wolf_array.py.

Contains:
- Rebin_Ops: Enum for rebin/downsampling operations (no wx dependency)
- NewArray: wx.Dialog to create a new WolfArray
- CropDialog: wx.Dialog to crop a 2D array
- IntValidator: wx.Validator for integer-only text controls

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 math
import string
import logging
import numpy as np

import wx

try:
    from ..PyTranslate import _
except ImportError:
[docs] def _(s): return s
from ..wolf_array._header_wolf import header_wolf # Rebin_Ops has been moved to wolfhece.wolf_array._base (no wx dependency) # Re-exported here for backward compatibility from ..wolf_array._base import Rebin_Ops
[docs] class NewArray(wx.Dialog): """ wx GUI interaction to create a new WolfArray Once filled, user/__init__ must call "init_from_new" """ def __init__(self, parent, mapviewer= None): super(NewArray, self).__init__(parent, title=_('New array'), size=(300, 300), style=wx.DEFAULT_DIALOG_STYLE | wx.TAB_TRAVERSAL | wx.OK)
[docs] self._mapviewer = mapviewer
self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) glsizer = self.CreateSeparatedButtonSizer(wx.OK) gSizer1 = wx.GridSizer(6, 2, 0, 0) glsizer.Insert(0, gSizer1)
[docs] self.m_staticText9 = wx.StaticText(self, wx.ID_ANY, u"dX [m]", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText9.Wrap(-1) gSizer1.Add(self.m_staticText9, 0, wx.ALL, 5)
[docs] self.dx = wx.TextCtrl(self, wx.ID_ANY, u"1", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.dx, 0, wx.ALL, 5)
[docs] self.m_staticText10 = wx.StaticText(self, wx.ID_ANY, u"dY [m]", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText10.Wrap(-1) gSizer1.Add(self.m_staticText10, 0, wx.ALL, 5)
[docs] self.dy = wx.TextCtrl(self, wx.ID_ANY, u"1", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.dy, 0, wx.ALL, 5)
[docs] self.m_staticText11 = wx.StaticText(self, wx.ID_ANY, u"NbX [-]", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText11.Wrap(-1) gSizer1.Add(self.m_staticText11, 0, wx.ALL, 5)
[docs] self.nbx = wx.TextCtrl(self, wx.ID_ANY, u"1", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.nbx, 0, wx.ALL, 5)
[docs] self.m_staticText12 = wx.StaticText(self, wx.ID_ANY, u"NbY [-]", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText12.Wrap(-1) gSizer1.Add(self.m_staticText12, 0, wx.ALL, 5)
[docs] self.nby = wx.TextCtrl(self, wx.ID_ANY, u"1", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.nby, 0, wx.ALL, 5)
[docs] self.m_staticText13 = wx.StaticText(self, wx.ID_ANY, u"Origin X [m]", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText13.Wrap(-1) gSizer1.Add(self.m_staticText13, 0, wx.ALL, 5)
[docs] self.ox = wx.TextCtrl(self, wx.ID_ANY, u"0", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.ox, 0, wx.ALL, 5)
[docs] self.m_staticText14 = wx.StaticText(self, wx.ID_ANY, u"Origin Y [m]", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText14.Wrap(-1) gSizer1.Add(self.m_staticText14, 0, wx.ALL, 5)
[docs] self.oy = wx.TextCtrl(self, wx.ID_ANY, u"0", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.oy, 0, wx.ALL, 5) # self.OK = wx.Button( self, wx.ID_ANY, u"Validate", wx.DefaultPosition, wx.DefaultSize, 0 ) # gSizer1.Add( self.OK, 0, wx.ALL, 5 ) sizer_hor = wx.BoxSizer(wx.HORIZONTAL)
[docs] self._sameas_button = wx.Button(self, wx.ID_ANY, u"Same as...", wx.DefaultPosition, wx.DefaultSize, 0)
sizer_hor.Add(self._sameas_button, 0, wx.ALL, 5) if self._mapviewer is not None: self._samezoom_parent = wx.Button(self, wx.ID_ANY, u"Current zoom...", wx.DefaultPosition, wx.DefaultSize, 0) sizer_hor.Add(self._samezoom_parent, 0, wx.ALL, 5) self._samezoom_parent.Bind(wx.EVT_BUTTON, self.on_samezoom_parent) glsizer.Add(sizer_hor, 1, wx.ALL, 1) self._sameas_button.Bind(wx.EVT_BUTTON, self.on_sameas) self.nbx.SetFocus() self.nbx.SelectAll() self.SetSizer(glsizer) self.Layout() self.Centre(wx.BOTH)
[docs] def on_samezoom_parent(self, event): """ Fill the fields with the same values as the parent """ if self._mapviewer is not None: with wx.TextEntryDialog(self, _('Round to the nearest x m ?'), _('Round to the nearest x m ?'), '10', style=wx.OK | wx.CANCEL) as dlg: if dlg.ShowModal() == wx.ID_CANCEL: return # the user changed their mind try: roundto = int(dlg.GetValue()) except: logging.error(_('The value must be a number')) return xmin, ymin, xmax, ymax = self._mapviewer.get_canvas_bounds() self.ox.SetValue(str((xmin // roundto) * roundto)) self.oy.SetValue(str((ymin // roundto) * roundto)) self.dx.SetValue(str(1)) self.dy.SetValue(str(1)) self.nbx.SetValue(str((math.ceil(xmax - xmin) // roundto) *roundto)) self.nby.SetValue(str((math.ceil(ymax - ymin) // roundto) *roundto))
[docs] def on_sameas(self, event): """ Fill the fields with the same values as a file array """ with wx.FileDialog(self, _("Choose a file"), wildcard="Wolf array files (*.bin;*.flt;*.tif,*.npy)|*.bin;*.flt;*.tif;*.npy", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind filename = fileDialog.GetPath() header = header_wolf() header.read_txt_header(filename) self.dx.SetValue(str(header.dx)) self.dy.SetValue(str(header.dy)) self.nbx.SetValue(str(header.nbx)) self.nby.SetValue(str(header.nby)) self.ox.SetValue(str(header.origx)) self.oy.SetValue(str(header.origy))
#FIXME : Generalize to 3D
[docs] class CropDialog(wx.Dialog): """ wx GUI interaction to crop 2D array's data Used in "read_data" of a WolfArray """ def __init__(self, parent, mapviewer=None): super(CropDialog, self).__init__(parent, title=_('Cropping array'), size=(300, 300), style=wx.DEFAULT_DIALOG_STYLE | wx.TAB_TRAVERSAL | wx.OK)
[docs] self._mapviewer = mapviewer
self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) glsizer = self.CreateSeparatedButtonSizer(wx.OK) gSizer1 = wx.GridSizer(6, 2, 0, 0) glsizer.Insert(0, gSizer1)
[docs] self.m_staticText9 = wx.StaticText(self, wx.ID_ANY, u"dX", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText9.Wrap(-1) gSizer1.Add(self.m_staticText9, 0, wx.ALL, 5)
[docs] self.dx = wx.TextCtrl(self, wx.ID_ANY, u"1", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.dx, 0, wx.ALL, 5)
[docs] self.m_staticText10 = wx.StaticText(self, wx.ID_ANY, u"dY", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText10.Wrap(-1) gSizer1.Add(self.m_staticText10, 0, wx.ALL, 5)
[docs] self.dy = wx.TextCtrl(self, wx.ID_ANY, u"1", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.dy, 0, wx.ALL, 5)
[docs] self.m_staticText11 = wx.StaticText(self, wx.ID_ANY, u"OrigX - lower left corner", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText11.Wrap(-1) gSizer1.Add(self.m_staticText11, 0, wx.ALL, 5)
[docs] self.ox = wx.TextCtrl(self, wx.ID_ANY, u"1", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.ox, 0, wx.ALL, 5)
[docs] self.m_staticText12 = wx.StaticText(self, wx.ID_ANY, u"OrigY - lower left corner", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText12.Wrap(-1) gSizer1.Add(self.m_staticText12, 0, wx.ALL, 5)
[docs] self.oy = wx.TextCtrl(self, wx.ID_ANY, u"1", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.oy, 0, wx.ALL, 5)
[docs] self.m_staticText13 = wx.StaticText(self, wx.ID_ANY, u"EndX - upper right corner", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText13.Wrap(-1) gSizer1.Add(self.m_staticText13, 0, wx.ALL, 5)
[docs] self.ex = wx.TextCtrl(self, wx.ID_ANY, u"0", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.ex, 0, wx.ALL, 5)
[docs] self.m_staticText14 = wx.StaticText(self, wx.ID_ANY, u"EndY - upper right corner", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticText14.Wrap(-1) gSizer1.Add(self.m_staticText14, 0, wx.ALL, 5)
[docs] self.ey = wx.TextCtrl(self, wx.ID_ANY, u"0", wx.DefaultPosition, wx.DefaultSize, style=wx.TE_CENTER)
gSizer1.Add(self.ey, 0, wx.ALL, 5) # self.OK = wx.Button( self, wx.ID_ANY, u"Validate", wx.DefaultPosition, wx.DefaultSize, 0 ) # gSizer1.Add( self.OK, 0, wx.ALL, 5 ) sizer_hor = wx.BoxSizer(wx.HORIZONTAL)
[docs] self.sameas_button = wx.Button(self, wx.ID_ANY, u"Same as...", wx.DefaultPosition, wx.DefaultSize, 0)
sizer_hor.Add(self.sameas_button, 0, wx.ALL, 5) if self._mapviewer is not None: self.samezoom_parent = wx.Button(self, wx.ID_ANY, u"Current zoom...", wx.DefaultPosition, wx.DefaultSize, 0) sizer_hor.Add(self.samezoom_parent, 0, wx.ALL, 5) self.samezoom_parent.Bind(wx.EVT_BUTTON, self.on_samezoom_parent) glsizer.Add(sizer_hor, 1, wx.ALL, 1) self.sameas_button.Bind(wx.EVT_BUTTON, self.on_sameas) self.ox.SetFocus() self.ox.SelectAll() self.SetSizer(glsizer) self.Layout() self.Centre(wx.BOTH)
[docs] def get_header(self): """ Return a header_wolf object with the values of the dialog """ myhead = header_wolf() myhead.origx = float(self.ox.Value) myhead.origy = float(self.oy.Value) myhead.dx = float(self.dx.Value) myhead.dy = float(self.dy.Value) myhead.nbx = int((float(self.ex.Value) - myhead.origx) / myhead.dx) myhead.nby = int((float(self.ey.Value) - myhead.origy) / myhead.dy) return myhead
[docs] def on_samezoom_parent(self, event): """ Fill the fields with the same values as the parent """ if self._mapviewer is not None: with wx.TextEntryDialog(self, _('Round to the nearest x m ?'), _('Round to the nearest x m ?'), '10', style=wx.OK | wx.CANCEL) as dlg: if dlg.ShowModal() == wx.ID_CANCEL: return # the user changed their mind try: roundto = int(dlg.GetValue()) except: logging.error(_('The value must be a number')) return xmin, ymin, xmax, ymax = self._mapviewer.get_canvas_bounds() self.ox.SetValue(str((xmin // roundto) * roundto)) self.oy.SetValue(str((ymin // roundto) * roundto)) # self.dx.SetValue(str(1)) # self.dy.SetValue(str(1)) self.ex.SetValue(str((math.ceil(xmax // roundto) * roundto))) self.ey.SetValue(str((math.ceil(ymax // roundto) * roundto)))
[docs] def on_sameas(self, event): """ Fill the fields with the same values as a file array """ with wx.FileDialog(self, _("Choose a file"), wildcard="Wolf array files (*.bin;*.flt;*.tif,*.npy)|*.bin;*.flt;*.tif;*.npy", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind filename = fileDialog.GetPath() header = header_wolf() header.read_txt_header(filename) # self.dx.SetValue(str(header.dx)) # self.dy.SetValue(str(header.dy)) self.ox.SetValue(str(header.origx)) self.oy.SetValue(str(header.origy)) self.ex.SetValue(str(header.origx + header.nbx * header.dx)) self.ey.SetValue(str(header.origy + header.nby * header.dy))
[docs] def get_crop(self): """ Return the crop values """ try: return [[float(self.ox.Value), float(self.oy.Value)], [float(self.ex.Value), float(self.ey.Value)]] except: logging.error(_('Values must be numbers')) return None
[docs] class IntValidator(wx.Validator): ''' Validates data as it is entered into the text controls. ''' #---------------------------------------------------------------------- def __init__(self): super(IntValidator, self).__init__() self.Bind(wx.EVT_CHAR, self.OnChar) #----------------------------------------------------------------------
[docs] def Clone(self): '''Required Validator method''' return IntValidator()
#----------------------------------------------------------------------
[docs] def Validate(self, win): return True
#----------------------------------------------------------------------
[docs] def TransferToWindow(self): return True
#----------------------------------------------------------------------
[docs] def TransferFromWindow(self): return True
#----------------------------------------------------------------------
[docs] def OnChar(self, event): keycode = int(event.GetKeyCode()) if keycode < 256: #print keycode key = chr(keycode) if key not in string.digits: return event.Skip()