You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

242 line
9.8 KiB

  1. import os
  2. from typing import Union
  3. import wx
  4. import wx.adv
  5. import wx.grid
  6. import wx.richtext
  7. import wx.lib.newevent
  8. from .positionwindow import PositionWindow
  9. from ..data import Position, AsterEphemerides, Event, MoonPhase
  10. from ..i18n import _, TIME_FORMAT, FULL_DATE_FORMAT
  11. from datetime import date
  12. FPB_HORIZONTAL = 0x4
  13. ComputeButtonEvent, EVT_COMPUTE_BUTTON = wx.lib.newevent.NewEvent()
  14. ExportButtonEvent, EVT_EXPORT_BUTTON = wx.lib.newevent.NewEvent()
  15. class ConfigPanel(wx.Panel):
  16. activate_position: bool
  17. position: Union[None, Position]
  18. compute_date: date
  19. timezone: int
  20. def __init__(self, parent, activate_position: bool = False, position: Position = None,
  21. compute_date: date = date.today(), timezone: int = 0):
  22. super(ConfigPanel, self).__init__(parent)
  23. self.activate_position = activate_position
  24. self.position = position
  25. self.compute_date = compute_date
  26. self.timezone = timezone
  27. self._position_checkbox = wx.CheckBox(self, label=_('Position:'))
  28. self._position_change_btn = wx.Button(self)
  29. self.update_position_btn_label()
  30. self._position_change_btn.SetToolTip(wx.ToolTip(_('Change the position')))
  31. self._position_checkbox.SetValue(activate_position)
  32. self._position_change_btn.Enable(activate_position)
  33. self.Bind(wx.EVT_CHECKBOX, self.on_position_checkbox, self._position_checkbox)
  34. self.Bind(wx.EVT_BUTTON, self.on_position_button, self._position_change_btn)
  35. date_lbl = wx.StaticText(self, label=_('Date:'))
  36. self._date_picker = wx.adv.DatePickerCtrl(self, dt=wx.DateTime(compute_date),
  37. style=wx.adv.DP_DEFAULT)
  38. self.Bind(wx.adv.EVT_DATE_CHANGED, self.date_changed, self._date_picker)
  39. timezone_lbl = wx.StaticText(self, label=_('Timezone:'))
  40. self._timezone_spin = wx.SpinCtrl(self, min=-23, max=23, value=str(timezone))
  41. self.Bind(wx.EVT_SPINCTRL, self.timezone_changed, self._timezone_spin)
  42. sizer = wx.FlexGridSizer(2, 5, 5)
  43. sizer.AddGrowableCol(1, 1)
  44. self._compute_button = wx.Button(self, label=_('&Compute'))
  45. self._export_button = wx.Button(self, label=_('E&xport PDF…'))
  46. self.Bind(wx.EVT_BUTTON, self.compute_button_clicked, self._compute_button)
  47. self.Bind(wx.EVT_BUTTON, self.export_button_clicked, self._export_button)
  48. sizer.AddMany([(self._position_checkbox, 0, wx.EXPAND),
  49. (self._position_change_btn, 0, wx.EXPAND),
  50. (date_lbl, 0, wx.EXPAND),
  51. (self._date_picker, 0, wx.EXPAND),
  52. (timezone_lbl, 0, wx.EXPAND),
  53. (self._timezone_spin, 0, wx.EXPAND)])
  54. main_sizer = wx.FlexGridSizer(3, 5, 5)
  55. main_sizer.AddGrowableCol(0, 2)
  56. main_sizer.AddMany([(sizer, 0, wx.EXPAND),
  57. (self._compute_button, 0, wx.EXPAND),
  58. (self._export_button, 0, wx.EXPAND)])
  59. self.SetSizer(main_sizer)
  60. def update_position_btn_label(self):
  61. self._position_change_btn.Label = str(self.position) if self.position is not None else _('Unknown')
  62. def enable_buttons(self, enable: bool = True):
  63. self._compute_button.Enable(enable)
  64. self._export_button.Enable(enable)
  65. def disable_buttons(self):
  66. self.enable_buttons(False)
  67. def on_position_checkbox(self, event):
  68. self.activate_position = event.IsChecked()
  69. self._position_change_btn.Enable(self.activate_position)
  70. if self.activate_position and self.position is None:
  71. self.on_position_button()
  72. def on_position_button(self, _=None):
  73. pos_win = PositionWindow(self, self.position)
  74. pos_win.ShowModal()
  75. self.position = pos_win.position
  76. if self.position is not None:
  77. self.update_position_btn_label()
  78. else:
  79. self._position_checkbox.SetValue(False)
  80. self._position_change_btn.Disable()
  81. def date_changed(self, _):
  82. self.compute_date = date.fromisoformat(self._date_picker.GetValue().FormatISODate())
  83. def timezone_changed(self, _):
  84. self.timezone = self._timezone_spin.GetValue()
  85. def compute_button_clicked(self, _):
  86. wx.PostEvent(self.GetEventHandler(), wx.PyCommandEvent(EVT_COMPUTE_BUTTON.typeId, self.GetId()))
  87. def export_button_clicked(self, _):
  88. wx.PostEvent(self.GetEventHandler(), wx.PyCommandEvent(EVT_EXPORT_BUTTON.typeId, self.GetId()))
  89. class MoonPhasePanel(wx.Panel):
  90. def __init__(self, parent):
  91. super(MoonPhasePanel, self).__init__(parent)
  92. self.moon_bitmap = wx.StaticBitmap(self, size=wx.Size(100, 100))
  93. self.moon_txt_l1 = wx.StaticText(self)
  94. self.moon_txt_l2 = wx.StaticText(self)
  95. self.moon_txt_l1.SetFont(wx.Font(wx.DEFAULT, wx.DEFAULT, wx.NORMAL, wx.BOLD))
  96. label_sizer = wx.GridSizer(3, 1, 0, 0)
  97. label_sizer.AddMany([(wx.StaticText(self), 0, wx.ALIGN_LEFT),
  98. (self.moon_txt_l1, 0, wx.ALIGN_LEFT | wx.EXPAND),
  99. (self.moon_txt_l2, 0, wx.ALIGN_LEFT | wx.EXPAND)])
  100. main_sizer = wx.FlexGridSizer(4, 0, 10)
  101. main_sizer.AddGrowableCol(0)
  102. main_sizer.AddGrowableCol(2)
  103. main_sizer.AddMany([(wx.StaticText(self), 0, wx.ALIGN_LEFT),
  104. (self.moon_bitmap, 0, wx.ALIGN_RIGHT),
  105. (label_sizer, 0, wx.ALIGN_LEFT)])
  106. self.SetSizer(main_sizer)
  107. def set_moon_phase(self, moon_phase: MoonPhase):
  108. moon_img = wx.Image(os.path.join(os.path.abspath(os.path.dirname(__file__)),
  109. '..', 'assets', 'moonphases', 'png',
  110. '.'.join([moon_phase.identifier.lower().replace('_', '-'),
  111. 'png'])))
  112. moon_img = moon_img.Scale(100, 100)
  113. bitmap = moon_img.ConvertToBitmap()
  114. self.moon_bitmap.SetBitmap(bitmap)
  115. self.moon_txt_l1.SetLabel(moon_phase.get_phase())
  116. self.moon_txt_l2.SetLabel(_('{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}').format(
  117. next_moon_phase=moon_phase.get_next_phase_name(),
  118. next_moon_phase_date=moon_phase.next_phase_date.strftime(FULL_DATE_FORMAT),
  119. next_moon_phase_time=moon_phase.next_phase_date.strftime(TIME_FORMAT)
  120. ))
  121. self.Layout()
  122. self.Fit()
  123. class ResultPanel(wx.Panel):
  124. ephemerides: [AsterEphemerides]
  125. events: [Event]
  126. moon_phase: Union[None, MoonPhase]
  127. def __init__(self, parent):
  128. super(ResultPanel, self).__init__(parent)
  129. self.moon_phase = None
  130. self.ephemerides = None
  131. self.events = None
  132. self._moon_phase_panel = MoonPhasePanel(self)
  133. self._grid_ephemerides = wx.grid.Grid(self)
  134. self._grid_ephemerides.SetDefaultCellAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
  135. self._grid_ephemerides.CreateGrid(numRows=0, numCols=3)
  136. self._grid_ephemerides.EnableEditing(False)
  137. self._grid_ephemerides.SetColLabelValue(0, _('Rise time'))
  138. self._grid_ephemerides.SetColLabelValue(1, _('Culmination time'))
  139. self._grid_ephemerides.SetColLabelValue(2, _('Set time'))
  140. self._grid_ephemerides.EnableEditing(False)
  141. self._list_events = wx.richtext.RichTextCtrl(self, size=wx.Size(0, 300),
  142. style=wx.richtext.RE_READONLY | wx.richtext.RE_MULTILINE)
  143. main_sizer = wx.FlexGridSizer(2, 1, 5, 5)
  144. main_sizer.AddGrowableCol(0)
  145. main_sizer.AddGrowableRow(1, 2)
  146. sizer = wx.FlexGridSizer(2, 5, 5)
  147. sizer.AddGrowableCol(0, 0)
  148. sizer.AddGrowableCol(1, 1)
  149. sizer.AddMany([(self._grid_ephemerides, 0, wx.EXPAND | wx.ALL),
  150. (self._list_events, 0, wx.EXPAND | wx.ALL)])
  151. main_sizer.AddMany([(self._moon_phase_panel, 0, wx.EXPAND | wx.ALL, 5),
  152. (sizer, 0, wx.EXPAND | wx.ALL)])
  153. self.SetSizer(main_sizer)
  154. def render(self):
  155. if self.moon_phase is not None:
  156. self._moon_phase_panel.set_moon_phase(self.moon_phase)
  157. if self._grid_ephemerides.NumberRows > 0:
  158. self._grid_ephemerides.DeleteRows(numRows=self._grid_ephemerides.NumberRows)
  159. if self.ephemerides is not None and len(self.ephemerides) > 0:
  160. for ephemeris in self.ephemerides:
  161. rise_time = ephemeris.rise_time.strftime(TIME_FORMAT) if ephemeris.rise_time is not None else ''
  162. culmination_time = ephemeris.culmination_time.strftime(TIME_FORMAT) if ephemeris.culmination_time is not None else ''
  163. set_time = ephemeris.set_time.strftime(TIME_FORMAT) if ephemeris.set_time is not None else ''
  164. self._grid_ephemerides.AppendRows()
  165. row = self._grid_ephemerides.NumberRows - 1
  166. self._grid_ephemerides.SetRowLabelValue(row, ephemeris.object.name)
  167. self._grid_ephemerides.SetCellValue(row, 0, rise_time)
  168. self._grid_ephemerides.SetCellValue(row, 1, culmination_time)
  169. self._grid_ephemerides.SetCellValue(row, 2, set_time)
  170. self._grid_ephemerides.Show(len(self.ephemerides) > 0)
  171. self._grid_ephemerides.AutoSize()
  172. if self.events is not None and len(self.events) > 0:
  173. content = ''
  174. for event in self.events:
  175. content += '\n' if content != '' else ''
  176. content += _('- {event_time}: {event_description}').format(
  177. event_time=event.start_time.strftime(TIME_FORMAT),
  178. event_description=event.get_description()
  179. )
  180. self._list_events.SetValue(content)
  181. else:
  182. self._list_events.SetValue(_('No events for this day.'))
  183. self.Layout()