|
- try:
- import wx
- except ModuleNotFoundError:
- raise ImportError('Module wxPython missing')
-
- import platform
- from typing import Union
- import webbrowser
- from threading import Thread
- from datetime import date
-
- from . import panel
-
- from ..data import Position, AsterEphemerides, Event, MoonPhase
- from ..exceptions import UnavailableFeatureError
- from ..ephemerides import get_ephemerides, get_moon_phase
- from ..events import search_events
- from ..version import VERSION
- from ..dumper import PdfDumper
- from ..i18n import _
-
- OS_DARWIN = 'Darwin'
- WINDOW_TITLE = 'Kosmorro'
- MIN_SIZE = wx.Size(700, 200) if platform.system() != OS_DARWIN else wx.Size(700, 160)
-
- # Events definitions
-
- myEVT_EPHEMERIDES_COMPUTED = wx.NewEventType()
- EVT_EPHEMERIDES_COMPUTED = wx.PyEventBinder(myEVT_EPHEMERIDES_COMPUTED, 1)
-
-
- class EphemeridesComputedEvent(wx.PyCommandEvent):
- def __init__(self, etype, eid, value=None):
- super(EphemeridesComputedEvent, self).__init__(etype, eid)
- self.value = value
-
-
- myEVT_MOON_PHASE_COMPUTED = wx.NewEventType()
- EVT_MOON_PHASE_COMPUTED = wx.PyEventBinder(myEVT_MOON_PHASE_COMPUTED, 1)
-
-
- class MoonPhaseComputedEvent(wx.PyCommandEvent):
- def __init__(self, etype, eid, value=None):
- super(MoonPhaseComputedEvent, self).__init__(etype, eid)
- self.value = value
-
-
- myEVT_EVENTS_COMPUTED = wx.NewEventType()
- EVT_EVENTS_COMPUTED = wx.PyEventBinder(myEVT_EVENTS_COMPUTED, 1)
-
-
- class EventsComputedEvent(wx.PyCommandEvent):
- def __init__(self, etype, eid, value=None):
- super(EventsComputedEvent, self).__init__(etype, eid)
- self.value = value
-
-
- # Computing threads
-
- class MoonPhaseComputer(Thread):
- def __init__(self, parent, compute_date: date):
- super(MoonPhaseComputer, self).__init__()
- self._parent = parent
- self.compute_date = compute_date
- self.moon_phase = None
-
- def run(self):
- self.moon_phase = get_moon_phase(self.compute_date)
- wx.PostEvent(self._parent, EphemeridesComputedEvent(myEVT_MOON_PHASE_COMPUTED, -1, self.moon_phase))
-
-
- class EphemeridesComputer(Thread):
- def __init__(self, parent, compute_date: date, position: Position, timezone: int):
- super(EphemeridesComputer, self).__init__()
- self._parent = parent
- self.compute_date = compute_date
- self.position = position
- self.timezone = timezone
- self.ephemerides = []
-
- def run(self):
- self.ephemerides = get_ephemerides(self.compute_date, self.position, self.timezone)
- wx.PostEvent(self._parent, EphemeridesComputedEvent(myEVT_EPHEMERIDES_COMPUTED, -1, self.ephemerides))
-
-
- class EventsComputer(Thread):
- def __init__(self, parent, compute_date: date, timezone: int):
- super(EventsComputer, self).__init__()
- self._parent = parent
- self.compute_date = compute_date
- self.timezone = timezone
- self.events = []
-
- def run(self):
- self.events = search_events(self.compute_date, self.timezone)
- wx.PostEvent(self._parent, EventsComputedEvent(myEVT_EVENTS_COMPUTED, -1, self.events))
-
-
- # Main window
-
- class MainWindow(wx.Frame):
- ephemerides: [AsterEphemerides]
- events: [Event]
- moon_phase: Union[None, MoonPhase]
-
- _progress_bar: wx.Gauge
-
- def __init__(self):
- super(MainWindow, self).__init__(None, title=WINDOW_TITLE,
- style=wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER | wx.MAXIMIZE_BOX))
-
- self.export_path = None
- self.config_panel = panel.ConfigPanel(self)
- self.Bind(panel.EVT_COMPUTE_BUTTON, self.compute)
- self.Bind(panel.EVT_EXPORT_BUTTON, self.export_button_clicked)
-
- self.result_presenter = panel.ResultPanel(self)
-
- self.sizer = wx.BoxSizer(wx.VERTICAL)
- self.sizer.AddMany([(self.config_panel, 0, wx.EXPAND | wx.ALL, 5),
- (self.result_presenter, 0, wx.EXPAND | wx.ALL, 5)])
-
- self.SetMinSize(MIN_SIZE)
-
- self.make_menu_bar()
- self.make_status_bar()
- self._progress_bar.Hide()
-
- self.SetSizer(self.sizer)
- self.resize(center=True)
-
- def make_status_bar(self):
- status_bar = self.CreateStatusBar()
- status_bar.SetFieldsCount(2)
- status_bar.SetStatusWidths([-2, -1])
- self._progress_bar = wx.Gauge(status_bar, -1, style=wx.GA_HORIZONTAL | wx.GA_SMOOTH)
- rect = status_bar.GetFieldRect(1)
- self._progress_bar.SetPosition((rect.x + 2, rect.y + 2))
- self._progress_bar.SetSize((rect.width - 4, rect.height - 4))
- self.result_presenter.Hide()
-
- def resize(self, center: bool = False):
- self.Fit()
- if center:
- self.Center()
-
- def make_menu_bar(self):
- menu_bar = wx.MenuBar()
-
- if platform.system() != OS_DARWIN:
- # "Application" menu
- app_menu = wx.Menu()
- exit_item = app_menu.Append(wx.ID_EXIT)
-
- # "Help" menu
- help_menu = wx.Menu()
- about_item = help_menu.Append(wx.ID_ABOUT)
-
- menu_bar.Append(app_menu, _('&Application'))
- menu_bar.Append(help_menu, _('&Help'))
- self.Bind(wx.EVT_MENU, self.on_exit, exit_item)
- else:
- # On macOS, put the menu items to the place they belong to: the application name menu
- apple_menu = menu_bar.OSXGetAppleMenu()
- about_item = apple_menu.Insert(0, wx.ID_ABOUT)
-
- self.Bind(wx.EVT_MENU, self.on_about, about_item)
- self.SetMenuBar(menu_bar)
-
- def on_exit(self, event):
- self.Close(True)
-
- def on_about(self, event):
- message = wx.MessageDialog(caption='Kosmorro version %s' % VERSION,
- message='© Jérôme Deuchnord - 2020\n\n' +
- _('This is a free software licensed under the '
- 'GNU Affero General Public License.'),
- style=wx.OK | wx.HELP | wx.ICON_INFORMATION,
- parent=self)
- message.SetOKLabel(_('Close'))
- message.SetHelpLabel(_('Website'))
- btn = message.ShowModal()
-
- if btn == wx.ID_HELP:
- webbrowser.open('http://kosmorro.space')
-
- def compute(self, __=None):
- # Reset data
- self.moon_phase = None
- self.ephemerides = None
- self.events = None
-
- self.config_panel.disable_buttons()
- self.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
-
- self.SetStatusText(_('Computing…'))
- self._progress_bar.SetValue(0)
- self._progress_bar.Show()
-
- thread_moon_phase = MoonPhaseComputer(self, self.config_panel.compute_date)
- thread_ephemerides = EphemeridesComputer(self, self.config_panel.compute_date, self.config_panel.position,
- self.config_panel.timezone)
- thread_events = EventsComputer(self, self.config_panel.compute_date, self.config_panel.timezone)
-
- self.Bind(EVT_MOON_PHASE_COMPUTED, self.on_moon_phase_computed)
- self.Bind(EVT_EPHEMERIDES_COMPUTED, self.on_ephemerides_computed)
- self.Bind(EVT_EVENTS_COMPUTED, self.on_events_computed)
-
- thread_moon_phase.start()
-
- if self.config_panel.activate_position and self.config_panel.position is not None:
- thread_ephemerides.start()
- else:
- self.ephemerides = []
- self._progress_bar.SetValue(50)
-
- thread_events.start()
-
- def export_button_clicked(self, __):
- self.export_path = self.get_export_file_path()
- if self.export_path is None:
- self.SetStatusText('Aborted.')
- return
-
- self.compute()
-
- def on_moon_phase_computed(self, event):
- self.moon_phase = event.value
- self._progress_bar.SetValue(self._progress_bar.GetValue() + 10)
-
- if self.is_all_computed():
- self.process_results()
-
- def on_ephemerides_computed(self, event):
- self.ephemerides = event.value
- self._progress_bar.SetValue(self._progress_bar.GetValue() + 50)
-
- if self.is_all_computed():
- self.process_results()
-
- def on_events_computed(self, event):
- self.events = event.value
- self._progress_bar.SetValue(self._progress_bar.GetValue() + 40)
-
- if self.is_all_computed():
- self.process_results()
-
- def is_all_computed(self) -> bool:
- return self.moon_phase is not None and \
- self.ephemerides is not None and \
- self.events is not None
-
- def process_results(self):
- self.config_panel.enable_buttons()
- self.SetStatusText('')
- self._progress_bar.Hide()
- self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
-
- if self.export_path is not None:
- self.export()
- else:
- self.show_results()
-
- def export(self, dumper_class = PdfDumper):
- ephemerides = self.ephemerides if len(self.ephemerides) > 0 else None
- dumper = dumper_class(date=self.config_panel.compute_date, ephemerides=ephemerides,
- events=self.events, moon_phase=self.moon_phase, timezone=self.config_panel.timezone,
- show_graph=True)
-
- try:
- with open(self.export_path, 'wb') as file:
- file.write(dumper.to_string())
- self.SetStatusText(_('PDF export saved in "{path}"!').format(path=self.export_path))
- except UnavailableFeatureError as error:
- wx.MessageDialog(self, error.msg, caption=_('Error while exporting your document'),
- style=wx.OK | wx.ICON_ERROR).ShowModal()
- finally:
- self.export_path = None
-
- def show_results(self):
- self.result_presenter.moon_phase = self.moon_phase
- self.result_presenter.ephemerides = self.ephemerides
- self.result_presenter.events = self.events
-
- self.result_presenter.render()
- self.result_presenter.Show()
- self.resize(center=True)
-
- def get_export_file_path(self) -> Union[None, str]:
- dialog = wx.FileDialog(parent=self, message=_('Please select an export file location'),
- wildcard='*.pdf', style=wx.FD_SAVE)
- if dialog.ShowModal() == wx.ID_OK:
- return dialog.GetPath()
- else:
- return None
-
-
- def start() -> bool:
- app = wx.App()
- app.SetAppName('Kosmorro')
- window = MainWindow()
-
- window.Show()
-
- return app.MainLoop() == 0
|