import os from typing import Union import wx import wx.adv import wx.grid import wx.richtext import wx.lib.newevent from .positionwindow import PositionWindow from ..data import Position, AsterEphemerides, Event, MoonPhase from ..i18n import _, TIME_FORMAT, FULL_DATE_FORMAT from datetime import date FPB_HORIZONTAL = 0x4 ComputeButtonEvent, EVT_COMPUTE_BUTTON = wx.lib.newevent.NewEvent() ExportButtonEvent, EVT_EXPORT_BUTTON = wx.lib.newevent.NewEvent() class ConfigPanel(wx.Panel): activate_position: bool position: Union[None, Position] compute_date: date timezone: int def __init__(self, parent, activate_position: bool = False, position: Position = None, compute_date: date = date.today(), timezone: int = 0): super(ConfigPanel, self).__init__(parent) self.activate_position = activate_position self.position = position self.compute_date = compute_date self.timezone = timezone self._position_checkbox = wx.CheckBox(self, label=_('Position:')) self._position_change_btn = wx.Button(self) self.update_position_btn_label() self._position_change_btn.SetToolTip(wx.ToolTip(_('Change the position'))) self._position_checkbox.SetValue(activate_position) self._position_change_btn.Enable(activate_position) self.Bind(wx.EVT_CHECKBOX, self.on_position_checkbox, self._position_checkbox) self.Bind(wx.EVT_BUTTON, self.on_position_button, self._position_change_btn) date_lbl = wx.StaticText(self, label=_('Date:')) self._date_picker = wx.adv.DatePickerCtrl(self, dt=wx.DateTime(compute_date), style=wx.adv.DP_DEFAULT) self.Bind(wx.adv.EVT_DATE_CHANGED, self.date_changed, self._date_picker) timezone_lbl = wx.StaticText(self, label=_('Timezone:')) self._timezone_spin = wx.SpinCtrl(self, min=-23, max=23, value=str(timezone)) self.Bind(wx.EVT_SPINCTRL, self.timezone_changed, self._timezone_spin) params_sizer = wx.FlexGridSizer(2, 5, 5) params_sizer.AddGrowableCol(1, 1) self._compute_button = wx.Button(self, label=_('&Compute')) self._export_button = wx.Button(self, label=_('E&xport PDF…')) self.Bind(wx.EVT_BUTTON, self.compute_button_clicked, self._compute_button) self.Bind(wx.EVT_BUTTON, self.export_button_clicked, self._export_button) params_sizer.AddMany([(self._position_checkbox, 0, wx.EXPAND), (self._position_change_btn, 0, wx.EXPAND), (date_lbl, 0, wx.EXPAND), (self._date_picker, 0, wx.EXPAND), (timezone_lbl, 0, wx.EXPAND), (self._timezone_spin, 0, wx.EXPAND)]) main_sizer = wx.FlexGridSizer(3, 5, 5) main_sizer.AddGrowableCol(0, 2) main_sizer.AddMany([(params_sizer, 0, wx.EXPAND), (self._compute_button, 0, wx.EXPAND), (self._export_button, 0, wx.EXPAND)]) self.SetSizer(main_sizer) def update_position_btn_label(self): self._position_change_btn.Label = str(self.position) if self.position is not None else _('Unknown') def enable_buttons(self, enable: bool = True): self._compute_button.Enable(enable) self._export_button.Enable(enable) def disable_buttons(self): self.enable_buttons(False) def on_position_checkbox(self, event): self.activate_position = event.IsChecked() self._position_change_btn.Enable(self.activate_position) if self.activate_position and self.position is None: self.on_position_button() def on_position_button(self, _=None): pos_win = PositionWindow(self, self.position) pos_win.ShowModal() self.position = pos_win.position if self.position is not None: self.update_position_btn_label() else: self._position_checkbox.SetValue(False) self._position_change_btn.Disable() def date_changed(self, _): self.compute_date = date.fromisoformat(self._date_picker.GetValue().FormatISODate()) def timezone_changed(self, _): self.timezone = self._timezone_spin.GetValue() def compute_button_clicked(self, _): wx.PostEvent(self.GetEventHandler(), wx.PyCommandEvent(EVT_COMPUTE_BUTTON.typeId, self.GetId())) def export_button_clicked(self, _): wx.PostEvent(self.GetEventHandler(), wx.PyCommandEvent(EVT_EXPORT_BUTTON.typeId, self.GetId())) class MoonPhasePanel(wx.Panel): def __init__(self, parent): super(MoonPhasePanel, self).__init__(parent) self.moon_bitmap = wx.StaticBitmap(self, size=wx.Size(100, 100)) self.moon_txt_l1 = wx.StaticText(self) self.moon_txt_l2 = wx.StaticText(self) self.moon_txt_l1.SetFont(wx.Font(wx.DEFAULT, wx.DEFAULT, wx.NORMAL, wx.BOLD)) label_sizer = wx.GridSizer(3, 1, 0, 0) label_sizer.AddMany([(wx.StaticText(self), 0, wx.ALIGN_LEFT), (self.moon_txt_l1, 0, wx.ALIGN_LEFT | wx.EXPAND), (self.moon_txt_l2, 0, wx.ALIGN_LEFT | wx.EXPAND)]) main_sizer = wx.FlexGridSizer(4, 0, 10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableCol(2) main_sizer.AddMany([(wx.StaticText(self), 0, wx.ALIGN_LEFT), (self.moon_bitmap, 0, wx.ALIGN_RIGHT), (label_sizer, 0, wx.ALIGN_LEFT)]) self.SetSizer(main_sizer) def set_moon_phase(self, moon_phase: MoonPhase): moon_img = wx.Image(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 'assets', 'moonphases', 'png', '.'.join([moon_phase.identifier.lower().replace('_', '-'), 'png']))) moon_img = moon_img.Scale(100, 100) bitmap = moon_img.ConvertToBitmap() self.moon_bitmap.SetBitmap(bitmap) self.moon_txt_l1.SetLabel(moon_phase.get_phase()) self.moon_txt_l2.SetLabel(_('{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}').format( next_moon_phase=moon_phase.get_next_phase_name(), next_moon_phase_date=moon_phase.next_phase_date.strftime(FULL_DATE_FORMAT), next_moon_phase_time=moon_phase.next_phase_date.strftime(TIME_FORMAT) )) self.Layout() self.Fit() class ResultPanel(wx.Panel): ephemerides: [AsterEphemerides] events: [Event] moon_phase: Union[None, MoonPhase] def __init__(self, parent): super(ResultPanel, self).__init__(parent) self.moon_phase = None self.ephemerides = None self.events = None self._moon_phase_panel = MoonPhasePanel(self) self._grid_ephemerides = wx.grid.Grid(self) self._grid_ephemerides.SetDefaultCellAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE) self._grid_ephemerides.CreateGrid(numRows=0, numCols=3) self._grid_ephemerides.EnableEditing(False) self._grid_ephemerides.SetColLabelValue(0, _('Rise time')) self._grid_ephemerides.SetColLabelValue(1, _('Culmination time')) self._grid_ephemerides.SetColLabelValue(2, _('Set time')) self._grid_ephemerides.EnableEditing(False) self._list_events = wx.richtext.RichTextCtrl(self, size=wx.Size(0, 300), style=wx.richtext.RE_READONLY | wx.richtext.RE_MULTILINE) main_sizer = wx.FlexGridSizer(2, 1, 5, 5) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1, 2) sizer = wx.FlexGridSizer(2, 5, 5) sizer.AddGrowableCol(0, 0) sizer.AddGrowableCol(1, 1) sizer.AddMany([(self._grid_ephemerides, 0, wx.EXPAND | wx.ALL), (self._list_events, 0, wx.EXPAND | wx.ALL)]) main_sizer.AddMany([(self._moon_phase_panel, 0, wx.EXPAND | wx.ALL, 5), (sizer, 0, wx.EXPAND | wx.ALL)]) self.SetSizer(main_sizer) def render(self): if self.moon_phase is not None: self._moon_phase_panel.set_moon_phase(self.moon_phase) if self._grid_ephemerides.NumberRows > 0: self._grid_ephemerides.DeleteRows(numRows=self._grid_ephemerides.NumberRows) if self.ephemerides is not None and len(self.ephemerides) > 0: for ephemeris in self.ephemerides: rise_time = ephemeris.rise_time.strftime(TIME_FORMAT) if ephemeris.rise_time is not None else '' culmination_time = ephemeris.culmination_time.strftime(TIME_FORMAT) if ephemeris.culmination_time is not None else '' set_time = ephemeris.set_time.strftime(TIME_FORMAT) if ephemeris.set_time is not None else '' self._grid_ephemerides.AppendRows() row = self._grid_ephemerides.NumberRows - 1 self._grid_ephemerides.SetRowLabelValue(row, ephemeris.object.name) self._grid_ephemerides.SetCellValue(row, 0, rise_time) self._grid_ephemerides.SetCellValue(row, 1, culmination_time) self._grid_ephemerides.SetCellValue(row, 2, set_time) self._grid_ephemerides.Show(len(self.ephemerides) > 0) self._grid_ephemerides.AutoSize() if self.events is not None and len(self.events) > 0: content = '' for event in self.events: content += '\n' if content != '' else '' content += _('- {event_time}: {event_description}').format( event_time=event.start_time.strftime(TIME_FORMAT), event_description=event.get_description() ) self._list_events.SetValue(content) else: self._list_events.SetValue(_('No events for this day.')) self.Layout()