Package Gnumed :: Package timelinelib :: Package wxgui :: Package frames :: Package mainframe :: Module mainframe
[frames] | no frames]

Source Code for Module Gnumed.timelinelib.wxgui.frames.mainframe.mainframe

  1  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg 
  2  # 
  3  # This file is part of Timeline. 
  4  # 
  5  # Timeline is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # Timeline is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>. 
 17   
 18   
 19  import os.path 
 20   
 21  import wx.lib.newevent 
 22   
 23  from timelinelib.canvas.data import TimePeriod 
 24  from timelinelib.config.dotfile import read_config 
 25  from timelinelib.config.paths import ICONS_DIR 
 26  from timelinelib.config.paths import LOCALE_DIR 
 27  from timelinelib.dataexport.timelinexml import export_db_to_timeline_xml 
 28  from timelinelib.db import db_open 
 29  from timelinelib.features.experimental.experimentalfeatures import ExperimentalFeatures 
 30  from timelinelib.meta.about import APPLICATION_NAME 
 31  from timelinelib.calendar.bosparanian.timetype import BosparanianTimeType 
 32  from timelinelib.calendar.num.timetype import NumTimeType 
 33  from timelinelib.utils import ex_msg 
 34  from timelinelib.wxgui.dialogs.changenowdate.view import ChangeNowDateDialog 
 35  from timelinelib.wxgui.dialogs.eraseditor.view import ErasEditorDialog 
 36  from timelinelib.wxgui.dialogs.setcategory.view import SetCategoryDialog 
 37  from timelinelib.wxgui.dialogs.timeeditor.view import TimeEditorDialog 
 38  from timelinelib.wxgui.frames.helpbrowserframe.helpbrowserframe import HelpBrowserFrame 
 39  from timelinelib.wxgui.frames.mainframe.mainframecontroller import LockedException 
 40  from timelinelib.wxgui.frames.mainframe.mainframecontroller import MainFrameController 
 41  from timelinelib.wxgui.timer import TimelineTimer 
 42  from timelinelib.wxgui.utils import display_error_message 
 43  from timelinelib.wxgui.utils import display_information_message 
 44  from timelinelib.wxgui.utils import WildcardHelper 
 45  import timelinelib.wxgui.frames.mainframe.guicreator as guic 
 46  from timelinelib.wxgui.frames.mainframe.controllerapi import MainFrameApiUsedByController 
 47  from timelinelib.wxgui.frames.mainframe.alertcontroller import AlertController 
 48  from timelinelib.wxgui.frames.mainframe.menucontroller import MenuController 
 49  from timelinelib.calendar.coptic.timetype import CopticTimeType 
 50  from timelinelib.calendar.pharaonic.timetype import PharaonicTimeType 
 51   
 52  CatsViewChangedEvent, EVT_CATS_VIEW_CHANGED = wx.lib.newevent.NewCommandEvent() 
 53   
 54   
55 -class MainFrame(wx.Frame, guic.GuiCreator, MainFrameApiUsedByController):
56
57 - def __init__(self, application_arguments):
58 self.config = read_config(application_arguments.get_config_file_path()) 59 wx.Frame.__init__(self, None, size=self.config.get_window_size(), 60 pos=self.config.get_window_pos(), 61 style=wx.DEFAULT_FRAME_STYLE, name="main_frame") 62 self.Bind(EVT_CATS_VIEW_CHANGED, self._on_cats_view_changed) 63 # To enable translations of wx stock items. 64 self.locale = wx.Locale(wx.LANGUAGE_DEFAULT) 65 self.locale.AddCatalogLookupPathPrefix(LOCALE_DIR) 66 self.locale.AddCatalog("wxstd") 67 self.help_browser = HelpBrowserFrame(self) 68 self.controller = MainFrameController(self, db_open, self.config) 69 self.menu_controller = MenuController() 70 self._set_initial_values_to_member_variables() 71 self._create_print_data() 72 self._set_experimental_features() 73 self._create_gui() 74 self.Maximize(self.config.window_maximized) 75 self.SetTitle(APPLICATION_NAME) 76 self.SetIcons(self._load_icon_bundle()) 77 self.main_panel.show_welcome_panel() 78 self.enable_disable_menus() 79 self.controller.on_started(application_arguments) 80 self._create_and_start_timer() 81 self.prev_time_period = None
82
83 - def DisplayErrorMessage(self, message):
85
86 - def DisplayStatus(self, message):
87 self.status_bar_adapter.set_text(message)
88 89 # API:s used by time types
90 - def week_starts_on_monday(self):
92
93 - def display_time_editor_dialog(self, time_type, initial_time, 94 handle_new_time_fn, title):
95 dialog = TimeEditorDialog(self, self.config, time_type, initial_time, title) 96 dialog.ShowModal() 97 result = dialog.GetReturnCode() 98 if result == wx.ID_OK: 99 handle_new_time_fn(dialog.GetTime()) 100 dialog.Destroy()
101
102 - def display_now_date_editor_dialog(self, handle_new_time_fn, title):
103 dialog = ChangeNowDateDialog(self, self.config, self.timeline, handle_new_time_fn, title) 104 dialog.Show()
105
106 - def save_time_period(self):
107 self.prev_time_period = self.main_panel.get_time_period()
108 109 # Concurrent editing
110 - def ok_to_edit(self):
111 try: 112 return self.controller.ok_to_edit() 113 except LockedException: 114 return False
115
116 - def get_lockfile_path(self):
117 return self.controller._get_lockpath()
118
119 - def edit_ends(self):
120 self.controller.edit_ends()
121
122 - def get_view_properties(self, ):
123 return self.main_panel.get_view_properties()
124
125 - def _on_cats_view_changed(self, evt):
126 self.main_panel.get_view_properties().change_view_cats_individually(evt.is_checked)
127 128 # Creation process methods
130 self.timeline = None 131 self.timeline_wildcard_helper = WildcardHelper( 132 _("Timeline files"), ["timeline", "ics"]) 133 self.images_svg_wildcard_helper = WildcardHelper( 134 _("SVG files"), ["svg"])
135
136 - def _create_print_data(self):
137 self.printData = wx.PrintData() 138 self.printData.SetPaperId(wx.PAPER_A4) 139 self.printData.SetPrintMode(wx.PRINT_MODE_PRINTER) 140 self.printData.SetOrientation(wx.LANDSCAPE)
141
142 - def _create_and_start_timer(self):
143 self.alert_dialog_open = False 144 self.timer = TimelineTimer(self) 145 self.timer.register(self._timer_tick) 146 self.timer.start(10000)
147
148 - def _timer_tick(self, evt):
149 self._handle_event_alerts()
150 153
154 - def _load_icon_bundle(self):
155 bundle = wx.IconBundle() 156 for size in ["16", "32", "48"]: 157 iconpath = os.path.join(ICONS_DIR, "%s.png" % size) 158 icon = wx.Icon(wx.Bitmap(wx.Image(iconpath))) 159 bundle.AddIcon(icon) 160 return bundle
161 162 # File Menu action handlers
163 - def _create_new_timeline(self, timetype=None):
164 path = self._get_file_path() 165 if path is not None: 166 self.controller.open_timeline(path, timetype)
167
169 self._create_new_timeline(BosparanianTimeType())
170
172 self._create_new_timeline(CopticTimeType())
173
175 self._create_new_timeline(PharaonicTimeType())
176
178 self._create_new_timeline(NumTimeType())
179
180 - def _get_file_path(self):
181 path = None 182 wildcard = self.timeline_wildcard_helper.wildcard_string() 183 dialog = wx.FileDialog(self, message=_("Create Timeline"), 184 wildcard=wildcard, style=wx.FD_SAVE) 185 if dialog.ShowModal() == wx.ID_OK: 186 path = self.timeline_wildcard_helper.get_path(dialog) 187 if os.path.exists(path): 188 msg_first_part = _("The specified timeline already exists.") 189 msg_second_part = _("Opening timeline instead of creating new.") 190 wx.MessageBox("%s\n\n%s" % (msg_first_part, msg_second_part), 191 _("Information"), 192 wx.OK | wx.ICON_INFORMATION, self) 193 dialog.Destroy() 194 return path
195
196 - def _create_new_dir_timeline(self):
197 dialog = wx.DirDialog(self, message=_("Create Timeline")) 198 if dialog.ShowModal() == wx.ID_OK: 199 self.controller.open_timeline(dialog.GetPath()) 200 dialog.Destroy()
201
202 - def _open_existing_timeline(self):
203 directory = "" 204 if self.timeline is not None: 205 directory = os.path.dirname(self.timeline.path) 206 wildcard = self.timeline_wildcard_helper.wildcard_string() 207 dialog = wx.FileDialog(self, message=_("Open Timeline"), 208 defaultDir=directory, 209 wildcard=wildcard, style=wx.FD_OPEN) 210 if dialog.ShowModal() == wx.ID_OK: 211 self.controller.open_timeline(dialog.GetPath()) 212 dialog.Destroy()
213
214 - def _save_as(self):
215 new_timeline_path = self._get_new_timeline_path_from_user() 216 self._save_timeline_to_new_path(new_timeline_path)
217
219 defaultDir = os.path.dirname(self.timeline.path) 220 wildcard_helper = WildcardHelper(_("Timeline files"), ["timeline"]) 221 wildcard = wildcard_helper.wildcard_string() 222 style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT 223 message = _("Save Timeline As") 224 dialog = wx.FileDialog(self, message=message, defaultDir=defaultDir, 225 wildcard=wildcard, style=style) 226 if dialog.ShowModal() == wx.ID_OK: 227 new_timeline_path = wildcard_helper.get_path(dialog) 228 else: 229 new_timeline_path = None 230 dialog.Destroy() 231 return new_timeline_path
232
233 - def _save_timeline_to_new_path(self, new_timeline_path):
234 if new_timeline_path is not None: 235 assert new_timeline_path.endswith(".timeline") 236 export_db_to_timeline_xml(self.timeline, new_timeline_path) 237 self.controller.open_timeline(new_timeline_path)
238
239 - def _window_on_close(self, event):
240 self.timer.stop() 241 self._save_application_config() 242 try: 243 if self.ok_to_edit(): 244 self.save_current_timeline_data() 245 finally: 246 self.edit_ends() 247 self.Destroy()
248
249 - def _save_application_config(self):
250 self.config.set_window_size(self.GetSize()) 251 self.config.set_window_pos(self.GetPosition()) 252 self.config.window_maximized = self.IsMaximized() 253 self.config.sidebar_width = self.main_panel.get_sidebar_width() 254 try: 255 self.config.write() 256 except IOError as ex: 257 friendly = _("Unable to write configuration file.") 258 msg = "%s\n\n%s" % (friendly, ex_msg(ex)) 259 display_error_message(msg, self)
260
262 if self.timeline: 263 self.main_panel.save_view_properties(self.timeline)
264 265 # Timeline Menu action handlers
267 event1, event2 = self._get_selected_events() 268 self._display_distance(event1.distance_to(event2))
269
270 - def _get_selected_events(self):
271 event_id_1, event_id_2 = self.main_panel.get_ids_of_two_first_selected_events() 272 event1 = self.timeline.find_event_with_id(event_id_1) 273 event2 = self.timeline.find_event_with_id(event_id_2) 274 return event1, event2
275
276 - def _display_distance(self, distance):
277 caption = _("Distance between selected events") 278 if distance is None: 279 distance_text = _("Events are overlapping or distance is 0") 280 else: 281 distance_text = self.timeline.get_time_type().format_delta(distance) 282 display_information_message(caption, distance_text)
283
284 - def _set_category(self):
285 dialog = SetCategoryDialog(self, self.timeline) 286 dialog.ShowModal() 287 dialog.Destroy() 288 self.main_panel.redraw_timeline()
289
291 selected_event_ids = self.main_panel.get_selected_event_ids() 292 dialog = SetCategoryDialog(self, self.timeline, selected_event_ids) 293 dialog.ShowModal() 294 dialog.Destroy()
295
296 - def _edit_eras(self):
297 dialog = ErasEditorDialog(self, self.timeline, self.config) 298 dialog.ShowModal() 299 dialog.Destroy() 300 self.main_panel.redraw_timeline()
301
302 - def _fit_all_events(self):
303 all_period = self._period_for_all_visible_events() 304 if all_period is None: 305 return 306 if all_period.is_period(): 307 self.main_panel.Navigate(lambda tp: tp.update(all_period.start_time, all_period.end_time)) 308 else: 309 self.main_panel.Navigate(lambda tp: tp.center(all_period.mean_time()))
310
312 try: 313 visible_events = self._all_visible_events() 314 if len(visible_events) > 0: 315 start = self._first_time(visible_events) 316 end = self._last_time(visible_events) 317 return TimePeriod(start, end).zoom(-1) 318 else: 319 return None 320 except ValueError as ex: 321 display_error_message(str(ex)) 322 return None
323
324 - def _all_visible_events(self):
325 all_events = self.timeline.get_all_events() 326 return self.main_panel.get_visible_events(all_events)
327
328 - def _first_time(self, events):
329 start_time = lambda event: event.get_start_time() 330 return start_time(min(events, key=start_time))
331
332 - def _last_time(self, events):
333 end_time = lambda event: event.get_end_time() 334 return end_time(max(events, key=end_time))
335
336 - def get_export_periods(self):
337 events = self._all_visible_events() 338 first_time = self._first_time(events) 339 last_time = self._last_time(events) 340 return self.main_panel.get_export_periods(first_time, last_time)
341 342 # Timer event handlers
343 - def _handle_event_alerts(self):
344 if self.timeline is None: 345 return 346 if self.alert_dialog_open: 347 return 348 self._display_events_alerts() 349 self.alert_dialog_open = False
350
351 - def _display_events_alerts(self):
352 self.alert_dialog_open = True 353 all_events = self.timeline.get_all_events() 354 AlertController(self).display_events_alerts(all_events, self.timeline.get_time_type())
355